Search Unity

Recalculating normal in surface shader

Discussion in 'Shaders' started by toomas, Apr 14, 2012.

  1. toomas

    toomas

    Joined:
    Jun 19, 2009
    Posts:
    16
    I am quite new to shaders and am having difficulties figuring out a way how to calculate normals in surface shader. The reason why I want to do this is because vertex program modiies their positions. This leaves me with outdated normals that gets used in surface program.

    void surf (Input IN, inout SurfaceOutput o) {
    o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
    o.Normal = ... // should use IN here, but how?
    }

    Ideas?
     
  2. Martin-Kraus

    Martin-Kraus

    Joined:
    Feb 18, 2011
    Posts:
    617
    Depends on the computation of the new vertex positions. Code?
     
  3. toomas

    toomas

    Joined:
    Jun 19, 2009
    Posts:
    16
    I'm trying to create a morph shader where morph is stored in mash tangents (delta from base mesh). Here's the shader code so far.

    Code (csharp):
    1. Shader "MorphShaderDiffuse" {
    2. Properties {
    3.     _Color ("Main Color", Color) = (1,1,1,1)
    4.     _MainTex ("Texture", 2D) = "white" {}
    5.     _Weight ("Morph weight", Range(0,1)) = 0
    6. }
    7. SubShader {
    8.     Tags { "RenderType" = "Opaque" }
    9.     CGPROGRAM
    10.     #pragma surface surf Lambert vertex:vert
    11.     struct Input {
    12.         float2 uv_MainTex;
    13.     };
    14.     fixed4 _Color;
    15.     float _Weight;
    16.     void vert (inout appdata_full v) {
    17.         v.vertex.xyz += v.tangent.xyz * _Weight;
    18.     }
    19.     sampler2D _MainTex;
    20.     void surf (Input IN, inout SurfaceOutput o) {
    21.         fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
    22.         o.Albedo = c.rgb;
    23.         o.Alpha = c.a;
    24.     }
    25.     ENDCG
    26. }
    27. Fallback "Diffuse"
    28. }
    29.  
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class ShaderMorphOne: MonoBehaviour {
    6.     void Awake() {
    7.         Mesh mesh = GetComponent<MeshFilter>().sharedMesh;
    8.         Vector4[] morph = new Vector4[mesh.vertexCount];
    9.         morph[2] = new Vector4(-0.2f, 0.5f, 2f, 0);
    10.         mesh.tangents = morph;
    11.     }
    12. }
    13.  
    The more I learn about how shaders work, it seems impossible to do this only inside a shader program. Maybe the only way is to use mesh.colors channel to pass normals, but I'd prefer not to use that. I've been fancing about using tangent.w somehow, but don't know how to pack all 3 components of a normal in there effectivly.
     
  4. Martin-Kraus

    Martin-Kraus

    Joined:
    Feb 18, 2011
    Posts:
    617
    I think you should also morph normals. (I assume you have normals for the original and the morphed mesh.) You probably require two components for morphing normals (e.g. two rotation angles) since the length of a normal vector doesn't matter.

    I haven't packed components in GLSL, but one way to pack 0 < x < 1 and 0 < y < 1 into one component might be z = round(128.0 * x) / 128.0 + y / 128.0. You can probably just use z as x (because y / 128.0 doesn't make a big difference to x); and use mod(z, 1.0 / 128.0) for y. Or something along these lines.
     
  5. toomas

    toomas

    Joined:
    Jun 19, 2009
    Posts:
    16
    Thanks Martin. I see your point, that's a clever way to do it. I'll try it out a bit later if I find time today. From the first look it seems that instead of "round", "floor" functions is more appropriate because you're using "mod".