- Home /
colorize mesh based on dot product
Assuming that we have an object and a 3D mesh. Is it possible to colorize the 3D mesh based on the value of the dot product produced when using the position of the 3D object and the normals of the mesh?
Normals of the mesh against what? the direction of the camera? the position of the object compared to the normal? is the colorization per face of the 3d mesh or the entire 3d mesh is set to one color based on a single dot product? some clarification would be appreciated. any of these can be done, but they'd be done in different places depending on which you're going for
Well I would like to colorize the faces of the mesh based on the dot product between the normals of the mesh against the position of an object. For example, I would like to colorize the faces of a mesh with a red color when the dot product between a game object and the normals is between 0 and 1 and with a blue color when the dot product of faces takes value between -1 and 0. Is this explanation O$$anonymous$$ or you need more clarifications?
Well, it's still not quite clear. What i understand is that you actually want to compare the worldspace normal of the mesh with the relative direction to another object. Basically just what normal lighting does (where the "other object" is actually the light).
However it's not clear if you want to do that with a shader or if you want to statically colorize the mesh or if the objects actually can move / rotate dynamically colorize the mesh via scripting.
Usually the shader approach is the one you want to use, especially if you need it for dynamic objects.
The ideal would be to work in real-time. I would like to change the object's position and direction and to colorize the mesh dynamically. Any suggestion how can I do this?
Answer by Bunny83 · Oct 09, 2015 at 04:01 PM
Well, writing a shader for that is quite simple. Here's an example:
// ColorizeMesh.shader
Shader "Custom/ColorizeMesh" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
_Color2 ("Color2", Color) = (1,1,1,1)
_ObjectPos ("ObjectPos", vector) = (1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
float3 worldPos;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
fixed4 _Color2;
float3 _ObjectPos;
void surf (Input IN, inout SurfaceOutputStandard o) {
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
float3 dir = _ObjectPos - IN.worldPos;
float t = 0.5 + sign(dot(dir, o.Normal))*0.5;
o.Albedo = c.rgb * lerp(_Color2.rgb, _Color.rgb, t);
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
This shader has an additional color property which is used when the triangle faces away from the given object. It also has an _ObjectPos property which need to be set via script to your desired object.
This can be done with a script like this:
// SetObjectPosition.cs
using UnityEngine;
using System.Collections;
public class SetObjectPosition : MonoBehaviour
{
public Transform target;
private Material mat;
int id = -1;
void Start()
{
var rend = GetComponent<Renderer>();
if (rend != null)
{
mat = rend.material;
id = Shader.PropertyToID("_ObjectPos");
}
}
void Update ()
{
if (target != null && mat != null)
mat.SetVector(id, target.position);
}
}
The object with the mesh should simply use a Material that uses the shader above. In addition just add the script to the same object. Don't forget to drag and drop your "other object" in the target variable of the script. In the material you can set the two colors to whatever you like. "Color" is the color that is facing the object, Color2 is the color that is facing away from the object.
Note: This shader uses the interpolated world position of each point and not just the direction between the two objects. If you want that you might want to change "_ObjectPos" into something like "_Direction". In the shader you could use "_Direction" ins$$anonymous$$d of "dir".
float t = 0.5 + sign(dot(_Direction, o.Normal))*0.5;
The script on the other hand need to calculate the direction ins$$anonymous$$d:
id = Shader.PropertyToID("_Direction");
mat.SetVector(id, target.position - transform.position);
Thanks for this. However, I have an additional question. Is it possible to colorize the variations of dot product? For example, I would like to colorize the faces of mesh in variations or red (from 0 to 1) if the dot product between the mesh and the face is between 0 and 1, and to colorize with variations of blue if the dot product is between -1 and 0.
Thank you. This works quite well. However, I have one more question. Is it possible to change the saturation of the colors given the value of the dot product? For example having a red color I would like to render the parts of mesh with with red = 0 when the dot product is 0 and with red (red = 1) when the dot product is 1 (of course I would like to render all the intermediate values between 0 and 1 of dot product). Similarly for the blue color when the dot product is between -1 (blue) to 0 (white).