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
return float(my math);
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.
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!
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.
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.
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!
managed to get it working for the most part.