Home Unity

Shader trouble.

ngon master
Offline / Send Message
almighty_gir ngon master
hi guys, i'm trying to emulate the GPU Gems 3:14 specularity in unity, here's the CG shader i've gotten so far, but i'm getting errors.
CGPROGRAM
		#pragma surface surf Lambert
		#include "UnityCG.cginc"
		#pragma target 3.0
		
		
		sampler2D _Beckmann;
		float _Roughness;
		float _Spec;
	
						
		float fresnelReflectance(SurfaceOutput s, half3 lightDir,half3 viewDir)
		{		
			half H =  dot (s.Normal, lightDir);
		
			float base = 1.0 - dot( viewDir, H );
			float exponential = pow( base, 5.0 );
			return exponential + 0.028 * ( 1.0 - exponential );
		}
		
		float PHBeckmann(SurfaceOutput s, half3 lightDir, float _Roughness)
		{	
			half H = dot(s.Normal, lightDir);
			float ndoth = dot(s.Normal, H);
  			float alpha = acos(dot(s.Normal, H));
  			float ta = tan(alpha);
  			float val = 1.0/(_Roughness*_Roughness*pow(ndoth,4.0))*exp(-(ta*ta)/(_Roughness*_Roughness));
  			return val;
		}
		
		float KS_Skin_Specular( SurfaceOutput s, float3 lightDir, float3 viewDir)
		   	
		
		{		
		  float result = 0.0;
		  float ndotl = dot( s.Normal, lightDir );
		  if( ndotl > 0.0 )
		{
	 
		   
		   float3 h = (lightDir + viewDir); 
		   float3 H = normalize(h);
		   float ndoth = dot( s.Normal, H );
		   float look = tex2D(_Beckmann,float2(ndoth,_Roughness));
		   float PH = pow( 2.0*look, 10.0 );
		   float F = fresnelReflectance( H, viewDir, 0.028 );
		   float frSpec = max( PH * F / dot( h, h ), 0 );
		   result = ndotl * _Spec * frSpec;
		 }
		 return result;
		}

the errors i'm getting are:
Shader error in 'Custom/BDRF_Testshader': Program 'vert_surf', 'fresnelReflectance': cannot implicitly convert from 'float3' to 'struct SurfaceOutput' (compiling for d3d11) 

now, i kind of understand it. it's telling me that it can't turn a float 3 (in this case, the viewDir and lightDir float3's) into a float. so i know what the error IS, i'm just not sure WHERE it is, like which part of the math i fucked up.

Replies

  • chronic
    Offline / Send Message
    chronic polycounter lvl 10
    doesn't seem like it should be doing that, you can try casting it explicitly

    return float(my math);
  • almighty_gir
    Offline / Send Message
    almighty_gir ngon master
    okay that kinda worked, now i'm getting "invalid operands on "*"" for "result = ndotl * _Spec * frSpec;"
  • cw
    Offline / Send Message
    cw polycounter lvl 17
    float fresnelReflectance(SurfaceOutput s, half3 lightDir,half3 viewDir)
    		{		
    			half H =  dot (s.Normal, lightDir);
    		
    			float base = 1.0 - dot( viewDir, H );
    			float exponential = pow( base, 5.0 );
    			return exponential + 0.028 * ( 1.0 - exponential );
    		}
    

    Try passing just the normal in rather than the whole struct? I started to try to work through your shader but I'm not too familiar with the unity bindings etc. So I'm not sure I can be a lot of help.

    I might go and read up on them and get a handle on the shaders though. :) Thanks for inspiring me! ;-)
  • cw
    Offline / Send Message
    cw polycounter lvl 17
    scanning the unity shader docs - it seems that you probably need to define a new lighting model rather than setting the surface parameters in the surf struct, or the compiler is going to apply lambert lighting on top of your values anyway.

    take the simple specular examle from here: http://docs.unity3d.com/Documentation/Components/SL-SurfaceShaderLightingExamples.html

    and splice in your skin spec functions, it should work?

    good luck!
  • almighty_gir
    Offline / Send Message
    almighty_gir ngon master
    okay cool, so i've managed to get it to this stage:
    CGPROGRAM
    		#pragma surface surf SkinSpecular
    		#include "UnityCG.cginc"
    		#pragma target 3.0
    		
    		sampler2D _Normal;
    		sampler2D _Beckmann;
    		float _Roughness;
    		float _Spec;
    		float4 _Diffcolor;
    	
    		float lightingsimplespecular(SurfaceOutput s, half3 lightDir, half3 viewDir, half atten)
    		{
    		
    				float3 HV(float3 lightDir, float3 viewDir) //syntax error on this line
    				{
    					float3 H = normalize(lightDir + viewDir);
    					return H;
    				}
    					
    				float ndotl(SurfaceOutput s, float3 lightDir)
    				{
    					float NL = dot(_Normal, lightDir);
    					return NL;			
    				}				
    				
    																																																
    				float fresnelReflectance(float3 lightDir,float3 viewDir)
    				{						
    					float base = 1.0 - dot( viewDir, ndotl );
    					float exponential = pow( base, 5.0 );
    					return exponential + 0.028 * ( 1.0 - exponential );
    				}
    				
    				float PHBeckmann(half3 lightDir, float _Roughness)
    				{	
    					half H = dot(_Normal, lightDir);
    					float ndoth = dot(_Normal, H);
    		  			float alpha = acos(dot(_Normal, H));
    		  			float ta = tan(alpha);
    		  			float val = 1.0/(_Roughness*_Roughness*pow(ndoth,4.0))*exp(-(ta*ta)/(_Roughness*_Roughness));
    		  			return val;
    				}
    				
    				float KS_Skin_Specular(float3 lightDir, float3 viewDir)		
    				{			  	
    				   float ndoth = dot(_Normal, HV );
    				   float look = tex2D(_Beckmann,float2(ndoth,_Roughness));
    				   float PH = pow( 2.0*look, 10.0 );
    				   float F = fresnelReflectance;
    				   float frSpec = max( PH * F / dot( h, h ), 0 );
    				   float spec = ndotl * _Spec * frSpec;
    				   return spec;		 
    				}
    		
    				float Finalspec()
    				{
    				   float FS = max(0.0, KS_Skin_Specular);
    				   return FS;
    				}
    				
    			
    					
    		half4 c;
    	    c.rgb = (s.Albedo * _Diffcolor * _LightColor0.rgb + _LightColor0.rgb * Finalspec) * (atten * 2); //syntax error on this line.
    	    c.a = s.Alpha;
    	    return c;    
    
    		}
    

    it's telling me there are syntax errors at lines which i've indicated in the above code. but i can't see them for the life of me.
  • equil
    no offense but you're making some pretty basic mistakes here.

    float FS = max(0.0, KS_Skin_Specular);

    here you're calling functions like variables which is such a fundamental mistake i'm honestly impressed you got this far.

    float ndoth = dot(_Normal, H);
    float spec = ndotl * _Spec * frSpec;

    not only are you calling a function as a variable here, but you're also calling a sampler(_Normal, _Spec) directly. this doesn't work. look into what tex2D() actually does. hopefully an example clears this up.

    float3 halfvector = H; <-- this is wrong
    float3 halfvector = H(lightDir, viewDir); <-- this is correct

    float ndoth = dot(tex2D(_Normal,UVs).xyz, H(lightDir, viewDir);

    in your actual shader you'll probably call s.Normal instead of tex2D() because of the way surface shaders are structured, and "UVs" would be replaced with your actual uv:s.

    I strongly suggest you to read through the shader documents in the unity manual, it's fairly easy to follow along (with a lot of examples) and covers a lot of ground.

    edit: and the shader compiler's error messages have always been really cryptic and confusing. a lot of the time the messages and line numbers it spits out are completely useless. unfortunately.
  • cw
    Offline / Send Message
    cw polycounter lvl 17
    ok so I pinched code from here:

    http://www.polycount.com/forum/showthread.php?t=96874
    Shader "Custom/test" {
        Properties {
          _MainTex ("Texture", 2D) = "white" {}
          _roughness ("roughness", Float) = 15
        }
        SubShader {
          Tags { "RenderType" = "Opaque" }
          CGPROGRAM
          #pragma surface surf GIR_SPEC
    	  #pragma target 3.0
    	  
    	  float _roughness;
    	float KelemenSzirmayKalosSpec( float3 V, float3 N, float3 L, float4 eccentricity, float rollOff, float4 weight )
    	{
    	float spec = 0;
    	
    	float cosne = dot( V, N );
    	float cosln = dot( N, L );
    	
    	if( cosln > 0 )
    	{
    		float3 h = L + V;
    		float3 H = normalize( h );
    		
    		float coseh = dot( H, V );
    		float cosnh = dot( H, N );
    		
    		float cosnhPow2 = cosnh*cosnh;
    		float ta = sqrt( 1 - cosnhPow2 ) / cosnh;
    		
    		
    		float cosnhPow4 = cosnhPow2*cosnhPow2;
    		float4 eccPow2 = eccentricity * eccentricity;
    		
    		float4 pH = exp( -( ta * ta ) / ( eccPow2 ) ) / ( eccPow2 * cosnhPow4 );
    		
    		// A Schlick Fresnel
    		float cosehPow = pow( 1 - coseh, 5.0 );
    		
    		float Ff = cosehPow + ( 1 - cosehPow ) / rollOff;
    		
    		float4 specCoeff = max ( ( pH * Ff ) / dot( h, h ), 0 );
    		
    		spec = saturate( cosln ) * dot( specCoeff, weight );
    	
    	}
    	
    	
    	return spec;
    	}
    	  
    	  
    	  
          half4 LightingGIR_SPEC (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten) {
          half3 h = normalize (lightDir + viewDir);
    
          half diff = max (0, dot (s.Normal, lightDir));
    
    	  float kspec = KelemenSzirmayKalosSpec( viewDir, s.Normal, lightDir,  float4( 1.0, .333, .167, .1 ), _roughness, float4( 1, .6, 0.07, 0.004 ) );
          half4 c;
          c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * kspec) * (atten * 2);
          c.a = s.Alpha;
          
          
          
          return c;
          }
    
          struct Input {
              float2 uv_MainTex;
          };
          sampler2D _MainTex;
          void surf (Input IN, inout SurfaceOutput o) {
              o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
          }
          ENDCG
        }
        Fallback "Diffuse"
      }
    
    

    Maybe that will help you out. One thing I noticed - the inputs seem to be per vertex not per pixel, not sure how to fix that in unity - worth googling to find out.

    As equil said, there's a few bloopers in your stuff there, so it's worth maybe attacking it in 2 stages - get something working in cg in a framework you are familiar with, and then deal with the unity plumbing as a separate task. It might make things simpler or go smoother.

    Good luck!
  • almighty_gir
    Offline / Send Message
    almighty_gir ngon master
    thanks for all the help guys,

    managed to get it working for the most part.
Sign In or Register to comment.