I’m so slow at getting surface shaders still. I have a speciel way of doing some fake anisotropic specular for water (to stretch the streak across the surface), which I used in the old shader system, and now I’m doing it with the surface shader approach. And I need the fresnel, I have caculated, to be available in the lightning model. And I have no idea how to.
I took the BlinnPhong model, and copied it into my shader as “LightningBlinnPhongAni”. It has these predefined parameters (SurfaceOutput s, fixed3 lightDir, fixed3 viewDir, fixed atten) but I also need my fresnel. And if I just add a “fixed fresnel” parameter to the function, I have no idea how to actually pass it to it, as this is not done by hand in surface shaders. Happens automatically.
Please help me with this probably simple question!
Just before your surface shader, simply declare the float you’re using for frensel amount and then use it inside your surface function.
float _Fresnel;
inline fixed4 LightingLightningBlinnPhongAni (SurfaceOutput s, fixed3 lightDir, fixed3 viewDir, fixed atten) {
…
}
Here’s a shader I wrote before with fresnel and anisotropic highlights in it and that uses a surface shader. Might be helpful
Shader "Anisotropic Specular" {
Properties {
_Color ("Main Color", Color) = (1,1,1,1)
_MainTex ("Diffuse (RGB)", 2D) = "white" {}
_SpecularTex ("Specular (R) Gloss (G) Null (B)", 2D) = "gray" {}
_BumpMap ("Normal (Normal)", 2D) = "bump" {}
_AnisoDirection ("Anisotropic Direction (RGB) Anisotropic Mask (A)", 2D) = "bump" {}
_AnisoOffset ("Anisotropic Highlight Offset", Range(-1,1)) = -0.2
_Cutoff ("Alpha Cut-Off Threshold", Range(0,1)) = 0.5
_Fresnel ("Fresnel Value", Float) = 0.028
}
SubShader{
Tags { "RenderType" = "TransparentCutout" }
CGPROGRAM
#pragma surface surf AnisoSpec exclude_path:prepass
#pragma target 3.0
struct SurfaceOutputAniso {
fixed3 Albedo;
fixed3 Normal;
fixed4 AnisoDir;
fixed3 Emission;
fixed3 Specular;
fixed Alpha;
};
struct Input
{
float2 uv_MainTex;
};
#define BASIC_LIGHTING \
viewDir = normalize(viewDir); \
lightDir = normalize(lightDir); \
float NdotL = dot(s.Normal, lightDir); \
float3 h = normalize(lightDir + viewDir)
inline float CalcFresnel (float3 viewDir, float3 h, float fresnelValue)
{
float fresnel = 1.0 - dot(viewDir, h);
fresnel = pow(fresnel, 5.0);
fresnel += fresnelValue * (1.0 - fresnel);
return fresnel;
}
inline float CalcAniso (float3 normal, float3 anisoDir, float3 h, float offset)
{
return max(0, sin(radians( (dot(normalize(normal + anisoDir), h) + offset) * 180 ) ));
}
inline float CalcSpec (float spec, float specLevel, float gloss, float fresnel)
{
return pow(spec, gloss * 128) * specLevel * fresnel;
}
inline float3 CalcFinalColor (float3 albedo, float3 lightColor, float NdotL, float spec, float atten)
{
float gamma = 2.2;
float invGamma = 1 / gamma;
return pow((((pow(albedo, gamma) * lightColor * saturate(NdotL)) * (lightColor * (1.0 - spec))) + (lightColor * spec)) * (atten * 2), invGamma);
}
float _Cutoff;
float _AnisoOffset;
float _Fresnel;
inline fixed4 LightingAnisoSpec (SurfaceOutputAniso s, fixed3 lightDir, fixed3 viewDir, fixed atten)
{
clip(s.Alpha - _Cutoff);
BASIC_LIGHTING;
float specBase = saturate(dot(s.Normal, h));
float aniso = CalcAniso(s.Normal, s.AnisoDir.rgb, h, _AnisoOffset);
float fresnel = CalcFresnel (viewDir, h, _Fresnel);
float spec = CalcSpec (lerp(specBase, aniso, s.AnisoDir.a), s.Specular.r, s.Specular.g, fresnel);
fixed4 c;
c.rgb = CalcFinalColor (s.Albedo, _LightColor0.rgb, NdotL, spec, atten);
c.a = 1;
return c;
}
sampler2D _MainTex, _SpecularTex, _BumpMap, _AnisoDirection;
void surf (Input IN, inout SurfaceOutputAniso o)
{
fixed4 albedo = tex2D(_MainTex, IN.uv_MainTex);
fixed4 anisoDir = tex2D(_AnisoDirection, IN.uv_MainTex).rgba;
anisoDir.rgb *= 2;
anisoDir.rgb -= 1;
o.Albedo = albedo.rgb;
o.Alpha = albedo.a;
o.AnisoDir = anisoDir.rgba;
o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_MainTex));
o.Specular = tex2D(_SpecularTex, IN.uv_MainTex).rgb;
}
ENDCG
}
FallBack "Transparent/Cutout/VertexLit"
But my fresnel isn’t a glabal property of the shader, but something I calculate based on a texture. So I don’t have the property outside of my surface shader function.
Like this:
void surf (Input IN, inout SurfaceOutput o) {
half fresnelFac = (dot( normalize(IN.viewDir), o.Normal ));
half3 fresnel = tex2D( _Fresnel, float2(fresnelFac,fresnelFac) );
}
And I need the calculated fresnel to be passed to my lightning model.
You can do exactly the same thing as declaring the float, just with a sampler2D.
sampler2D _Fresnel;
inline fixed4 LightingLightningBlinnPhongAni (SurfaceOutput s, fixed3 lightDir, fixed3 viewDir, fixed atten) {
half fresnelFac = dot( normalize(viewDir), s.Normal );
half3 fresnel = tex2D( _Fresnel, float2(fresnelFac) );
}
So long as you define something before the surface shader’s lighting function, you can use it inside of the lighting function.
But I’m also using this fresnel property in my surface shader function (in my o.Emission). And now this function is giving me a compile error, talking about the undefined fresnel variable (which I moved to the lightning model function). When i use “inline” in my lightning model function declaration, where excatly is it inset in my code? I need to use the fresnel in both functions, but only want to calculate it once
Thanks a bunch for your continuous help!!!
You can simulate the effects of o.Emission by adding it to the result of your lighting equation after you’ve finished it.
inline fixed4 LightingLightningBlinnPhongAni (SurfaceOutputAniso s, fixed3 lightDir, fixed3 viewDir, fixed atten)
{
fixed4 c;
c.rgb = ((s.Albedo * _LightColor0.rgb * NdotL) + (lightColor * spec)) * (atten * 2);
c.a = 1;
c.rgb += [b][i]Add what you intended for your emission here.[/i][/b]
return c;
}
Hmm, this is beginning to sound like trying to circumventing the surface shader system, instead of using it as intended. And it gives me new problems. I use a calculated reflcol.rgb in my emission as well, so now this is the problem, as I can’t pass this to the lightning model. And this is calculated like this:
float3 worldRefl = WorldReflectionVector (IN, o.Normal);
fixed4 reflcol = texCUBE (_Cube, worldRefl);
-and I can’t calculate this in the lightning model, as it uses IN as input parameter.
Is there really no “right way” of passing whatever extra parameters you need to the lightning model? It really feels that surface shaders makes it easier to do the simple shaders, which we already have built-in, and harder to make special shaders, which are what we need to make…
You can define your own SurfaceOutput struct and add in the value you want.
Then it’ll get passed along to the surface shader lighting function along with Albedo, Specular, Gloss, Emission, etc…
If you post your entire code, it’ll be easier to see what you’re doing and what results you want.
Thank you, this was it. This works, and feels right at the same time. And is an obvious solution once contemplated. Stupid me.
I somehow love you… Thanks!
And sorry for not posting the code, but that would make me have to take it to a higher place, to get permission for that, which I didn’t want to. And I hoped the question was general enough for a general solution. Which it were…