Trying to create custom standard shader with geometric specular aliasing

We’re trying to create a specific shader for VR use.
The shader should mimic Unity’s standard shader except with only these 4 settings:

  1. albedo with color selector
  2. metallic with smoothness slider
  3. normal map with float value
  4. occlusion with slider

We need the shader to interpolate the normal twice, once with the centroid and once without. Then compare them.

Not proficient at shader programming, but 42-44 of this doc seems to have a hint

Has anyone already done this or understand how to implement it?

1 Like

The code on page 42 is related to anisotropic blinn-phong specular. Unity uses isotropic GGX, so that code doesn’t really work if you want to match Unity’s lighting style. The main benefit of using anisotropic specular for VR is it’s use in conjunction with with specular AA, which is what the previous ~15 pages are devoted to. That requires a bit more work than just a shader change, but an entire workflow overhaul to support. Technically it doesn’t even require any shader changes, just some preprocessing of the smoothness map.

It can be done on your own if you want. Stephen Hill and Matt Pettineo have some excellent write ups and example code you can use to implement specular AA yourself. If you don’t want to do it yourself the Alloy framework asset is the only asset I can think off that implements specular AA, though I don’t believe it uses anisotropic GGX by default.

The technique described on page 43 is trivial to implement in a surface shader. You can almost literally copy and paste the code into the surf function and it’ll work.

// add this to your Input struct
// float3 worldNormal; INTERNAL_DATA

// assign the o.Smoothness value to your content set value prior to this code
// ie. o.Smoothness = metallicGloss.a * _SmoothnessScale;
float3 vNormalWsDdx = ddx( IN.worldNormal.xyz );
float3 vNormalWsDdy = ddy( IN.worldNormal.xyz );
float flGeometricRoughnessFactor = pow( saturate( max( dot( vNormalWsDdx.xyz, vNormalWsDdx.xyz ), dot( vNormalWsDdy.xyz, vNormalWsDdy.xyz ) ) ), 0.333 );
o.Smoothness= min( o.Smoothness, 1.0 - flGeometricRoughnessFactor ); // Ensure we don’t double-count roughness if normal map encodes geometric roughness

The technique in page 44 is a bit more involved. It can’t be done with a surface shader directly as there’s no control over how data is passed from the vertex shader to the fragment shader. It’s a huge pain to modify the standard shader directly as to implement this I think requires modifying 5-6 files. The easiest solution is to modify the generated code from a surface shader. This isn’t hard, but surface shaders’ generated code is super ugly to look at so it can be more of a pain than it needs to be just from trying to figure out all the places you need to touch.

Honestly, the technique from page 43 can fix 90% of the problems on it’s own. My personal experience has been if you’re needing the technique from page 44, this is more a content issue than something that should be fixed in the shader since it’s caused by very thin geometry with extreme normal changes.

edit: typo, should be a min, not max, for the smoothness

One other thing about centroid sampling. It’s semi-broken in Unity for reasons no one has looked into.

Below is a video showing white lines where centroid sample and the regular interpolated sample are different. It flickers constantly. This can sometimes be worse than the aliasing it’s fixing.
Epilepsy warning, flashing! 3217152--246453--centroid flicker2.gif

Modified a surface shader to implement centroid normals and geometric roughness. Here’s a comparison using only the geometric roughness:

Notice the fingers of the hands look smoother (and slightly brighter), but the top of the hand still has some aliasing from the normal map.
Notice the aliasing on the under side of the shoulder and arm pit is reduced, but not completely removed. Also notice the highlight on the front side of the shoulder is aliased. This is from HDR being enabled. Disabling HDR makes that look significantly better.

Centroid sampling can be toggled on and off, but only changes one or two pixels for this model … when it works at all. You can try enabling it to see if it helps, but I simply ignore it for now as it’s not reliable. It is unfortunate since when I implemented this in a custom engine it did wonders for very thin geometry details.

VR Standard Shader

Shader "VRStandard" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _GlossMapScale("Smoothness Scale", Range(0.0, 1.0)) = 1.0
        [NoScaleOffset] _MetallicGlossMap("Metallic", 2D) = "white" {}
        _BumpScale("Scale", Float) = 1.0
        [NoScaleOffset] _BumpMap("Normal Map", 2D) = "bump" {}
        _OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0
        [NoScaleOffset] _OcclusionMap("Occlusion", 2D) = "white" {}
        [Toggle(_CENTROIDNORMAL)] _CentroidNormal ("Enable Vertex Normal Centroid Sampling Fixup", Float) = 0
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200
      
      
    // ------------------------------------------------------------
    // Surface shader code generated out of a CGPROGRAM block:
  

    // ---- forward rendering base pass:
    Pass {
        Name "FORWARD"
        Tags { "LightMode" = "ForwardBase" }

CGPROGRAM
// compile directives
#pragma vertex vert_surf
#pragma fragment frag_surf
#pragma target 5.0
#pragma multi_compile_instancing
#pragma shader_feature _CENTROIDNORMAL
#pragma multi_compile_fog
#pragma multi_compile_fwdbase
#include "HLSLSupport.cginc"
#include "UnityShaderVariables.cginc"
#include "UnityShaderUtilities.cginc"
// -------- variant for: <when no other keywords are defined>
#if !defined(INSTANCING_ON)
// Surface shader code generated based on:
// vertex modifier: 'vert'
// writes to per-pixel normal: YES
// writes to emission: no
// writes to occlusion: YES
// needs world space reflection vector: no
// needs world space normal vector: YES
// needs screen space position: no
// needs world space position: no
// needs view direction: no
// needs world space view direction: no
// needs world space position for lighting: YES
// needs world space view direction for lighting: YES
// needs world space view direction for lightmaps: no
// needs vertex color: no
// needs VFACE: no
// passes tangent-to-world matrix to pixel shader: YES
// reads from normal: no
// 1 texcoords actually used
//   float2 _MainTex
#define UNITY_PASS_FORWARDBASE
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "UnityPBSLighting.cginc"
#include "AutoLight.cginc"

#define INTERNAL_DATA half3 internalSurfaceTtoW0; half3 internalSurfaceTtoW1; half3 internalSurfaceTtoW2;
#define WorldReflectionVector(data,normal) reflect (data.worldRefl, half3(dot(data.internalSurfaceTtoW0,normal), dot(data.internalSurfaceTtoW1,normal), dot(data.internalSurfaceTtoW2,normal)))
#define WorldNormalVector(data,normal) fixed3(dot(data.internalSurfaceTtoW0,normal), dot(data.internalSurfaceTtoW1,normal), dot(data.internalSurfaceTtoW2,normal))

#ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING
#endif
/* UNITY: Original start of shader */
        // Physically based Standard lighting model, and enable shadows on all light types
        //#pragma surface surf Standard fullforwardshadows vertex:vert

        // Use shader model 3.0 target, to get nicer looking lighting
        //#pragma target 5.0

        #include "UnityStandardUtils.cginc"

        sampler2D _MainTex;
        sampler2D _MetallicGlossMap;
        sampler2D _BumpMap;
        sampler2D _OcclusionMap;

        struct Input {
            float2 uv_MainTex;
            float3 worldNormal; INTERNAL_DATA
        };

        fixed4 _Color;
        half _GlossMapScale;
        half _BumpScale;
        half _OcclusionStrength;

        void surf (Input IN, inout SurfaceOutputStandard o) {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;

            fixed4 mg = tex2D(_MetallicGlossMap, IN.uv_MainTex);
            o.Metallic = mg.r;
            o.Smoothness = mg.a * _GlossMapScale;

            float3 vNormalWsDdx = ddx_fine( IN.worldNormal.xyz );
            float3 vNormalWsDdy = ddy_fine( IN.worldNormal.xyz );
            float flGeometricRoughnessFactor = pow( saturate( max( dot( vNormalWsDdx.xyz, vNormalWsDdx.xyz ), dot( vNormalWsDdy.xyz, vNormalWsDdy.xyz ) ) ), 0.333 );
            o.Smoothness = min( o.Smoothness, 1.0 - flGeometricRoughnessFactor ); // Ensure we don’t double-count roughness if normal map encodes geometric roughness

            half occ = tex2D(_OcclusionMap, IN.uv_MainTex).g;
            o.Occlusion = LerpOneTo (occ, _OcclusionStrength);

            half3 normal = UnpackScaleNormal(tex2D(_BumpMap, IN.uv_MainTex), _BumpScale);
            o.Normal = normal;
        }
      

// vertex-to-fragment interpolation data
// no lightmaps:
#ifndef LIGHTMAP_ON
struct v2f_surf {
    float4 pos : SV_POSITION;
    float2 pack0 : TEXCOORD0; // _MainTex
    float4 tSpace0 : TEXCOORD1;
    float4 tSpace1 : TEXCOORD2;
    float4 tSpace2 : TEXCOORD3;
    #if defined(_CENTROIDNORMAL)
    float3 centroidNormal : TEXCOORD4_centroid;
    #endif
    #if UNITY_SHOULD_SAMPLE_SH
    half3 sh : TEXCOORD5; // SH
    #endif
    UNITY_SHADOW_COORDS(6)
    UNITY_FOG_COORDS(7)
    #if SHADER_TARGET >= 30
    float4 lmap : TEXCOORD8;
    #endif
    UNITY_VERTEX_INPUT_INSTANCE_ID
    UNITY_VERTEX_OUTPUT_STEREO
};
#endif
// with lightmaps:
#ifdef LIGHTMAP_ON
struct v2f_surf {
    float4 pos : SV_POSITION;
    float2 pack0 : TEXCOORD0; // _MainTex
    float4 tSpace0 : TEXCOORD1;
    float4 tSpace1 : TEXCOORD2;
    float4 tSpace2 : TEXCOORD3;
    #if defined(_CENTROIDNORMAL)
    float3 centroidNormal : TEXCOORD4_centroid;
    #endif
    float4 lmap : TEXCOORD5;
    UNITY_SHADOW_COORDS(6)
    UNITY_FOG_COORDS(7)
    UNITY_VERTEX_INPUT_INSTANCE_ID
    UNITY_VERTEX_OUTPUT_STEREO
};
#endif
float4 _MainTex_ST;

// vertex shader
v2f_surf vert_surf (appdata_full v) {
    UNITY_SETUP_INSTANCE_ID(v);
    v2f_surf o;
    UNITY_INITIALIZE_OUTPUT(v2f_surf,o);
    UNITY_TRANSFER_INSTANCE_ID(v,o);
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
    o.pos = UnityObjectToClipPos(v.vertex);
    o.pack0.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
    float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
    float3 worldNormal = UnityObjectToWorldNormal(v.normal);
    float3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
    float tangentSign = v.tangent.w * unity_WorldTransformParams.w;
    float3 worldBinormal = cross(worldNormal, worldTangent) * tangentSign;
    o.tSpace0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
    o.tSpace1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
    o.tSpace2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);
    #if defined(_CENTROIDNORMAL)
    o.centroidNormal = worldNormal;
    #endif
    #ifdef DYNAMICLIGHTMAP_ON
    o.lmap.zw = v.texcoord2.xy * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
    #endif
    #ifdef LIGHTMAP_ON
    o.lmap.xy = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
    #endif

    // SH/ambient and vertex lights
    #ifndef LIGHTMAP_ON
        #if UNITY_SHOULD_SAMPLE_SH
            o.sh = 0;
            // Approximated illumination from non-important point lights
            #ifdef VERTEXLIGHT_ON
                o.sh += Shade4PointLights (
                    unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
                    unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,
                    unity_4LightAtten0, worldPos, worldNormal);
            #endif
            o.sh = ShadeSHPerVertex (worldNormal, o.sh);
        #endif
    #endif // !LIGHTMAP_ON

    UNITY_TRANSFER_SHADOW(o,v.texcoord1.xy); // pass shadow coordinates to pixel shader
    UNITY_TRANSFER_FOG(o,o.pos); // pass fog coordinates to pixel shader
    return o;
}

// fragment shader
fixed4 frag_surf (v2f_surf IN) : SV_Target {
    UNITY_SETUP_INSTANCE_ID(IN);
    // prepare and unpack data
    Input surfIN;
    UNITY_INITIALIZE_OUTPUT(Input,surfIN);
    surfIN.uv_MainTex.x = 1.0;
    surfIN.uv_MainTex = IN.pack0.xy;
    float3 worldPos = float3(IN.tSpace0.w, IN.tSpace1.w, IN.tSpace2.w);
    #ifndef USING_DIRECTIONAL_LIGHT
        fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));
    #else
        fixed3 lightDir = _WorldSpaceLightPos0.xyz;
    #endif
    fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
    surfIN.worldNormal = float3(IN.tSpace0.z, IN.tSpace1.z, IN.tSpace2.z);
    surfIN.internalSurfaceTtoW0 = IN.tSpace0.xyz;
    surfIN.internalSurfaceTtoW1 = IN.tSpace1.xyz;
    surfIN.internalSurfaceTtoW2 = IN.tSpace2.xyz;
    #ifdef UNITY_COMPILER_HLSL
    SurfaceOutputStandard o = (SurfaceOutputStandard)0;
    #else
    SurfaceOutputStandard o;
    #endif
    o.Albedo = 0.0;
    o.Emission = 0.0;
    o.Alpha = 0.0;
    o.Occlusion = 1.0;
    fixed3 normalWorldVertex = fixed3(0,0,1);

    #if defined(_CENTROIDNORMAL)
    if ( dot( surfIN.worldNormal, surfIN.worldNormal ) >= 1.01 )
    {
        IN.tSpace0.z = IN.centroidNormal.x;
        IN.tSpace1.z = IN.centroidNormal.y;
        IN.tSpace2.z = IN.centroidNormal.z;
    }
    #endif

    // call surface function
    surf (surfIN, o);

    // compute lighting & shadowing factor
    UNITY_LIGHT_ATTENUATION(atten, IN, worldPos)
    fixed4 c = 0;
    fixed3 worldN;
    worldN.x = dot(IN.tSpace0.xyz, o.Normal);
    worldN.y = dot(IN.tSpace1.xyz, o.Normal);
    worldN.z = dot(IN.tSpace2.xyz, o.Normal);
    o.Normal = normalize(worldN);

    // Setup lighting environment
    UnityGI gi;
    UNITY_INITIALIZE_OUTPUT(UnityGI, gi);
    gi.indirect.diffuse = 0;
    gi.indirect.specular = 0;
    gi.light.color = _LightColor0.rgb;
    gi.light.dir = lightDir;
    // Call GI (lightmaps/SH/reflections) lighting function
    UnityGIInput giInput;
    UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput);
    giInput.light = gi.light;
    giInput.worldPos = worldPos;
    giInput.worldViewDir = worldViewDir;
    giInput.atten = atten;
    #if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON)
        giInput.lightmapUV = IN.lmap;
    #else
        giInput.lightmapUV = 0.0;
    #endif
    #if UNITY_SHOULD_SAMPLE_SH
        giInput.ambient = IN.sh;
    #else
        giInput.ambient.rgb = 0.0;
    #endif
    giInput.probeHDR[0] = unity_SpecCube0_HDR;
    giInput.probeHDR[1] = unity_SpecCube1_HDR;
    #if defined(UNITY_SPECCUBE_BLENDING) || defined(UNITY_SPECCUBE_BOX_PROJECTION)
        giInput.boxMin[0] = unity_SpecCube0_BoxMin; // .w holds lerp value for blending
    #endif
    #ifdef UNITY_SPECCUBE_BOX_PROJECTION
        giInput.boxMax[0] = unity_SpecCube0_BoxMax;
        giInput.probePosition[0] = unity_SpecCube0_ProbePosition;
        giInput.boxMax[1] = unity_SpecCube1_BoxMax;
        giInput.boxMin[1] = unity_SpecCube1_BoxMin;
        giInput.probePosition[1] = unity_SpecCube1_ProbePosition;
    #endif
    LightingStandard_GI(o, giInput, gi);

    // realtime lighting: call lighting function
    c += LightingStandard (o, worldViewDir, gi);
    UNITY_APPLY_FOG(IN.fogCoord, c); // apply fog
    UNITY_OPAQUE_ALPHA(c.a);
    return c;
}


#endif

// -------- variant for: INSTANCING_ON
#if defined(INSTANCING_ON)
// Surface shader code generated based on:
// vertex modifier: 'vert'
// writes to per-pixel normal: YES
// writes to emission: no
// writes to occlusion: YES
// needs world space reflection vector: no
// needs world space normal vector: YES
// needs screen space position: no
// needs world space position: no
// needs view direction: no
// needs world space view direction: no
// needs world space position for lighting: YES
// needs world space view direction for lighting: YES
// needs world space view direction for lightmaps: no
// needs vertex color: no
// needs VFACE: no
// passes tangent-to-world matrix to pixel shader: YES
// reads from normal: no
// 1 texcoords actually used
//   float2 _MainTex
#define UNITY_PASS_FORWARDBASE
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "UnityPBSLighting.cginc"
#include "AutoLight.cginc"

#define INTERNAL_DATA half3 internalSurfaceTtoW0; half3 internalSurfaceTtoW1; half3 internalSurfaceTtoW2;
#define WorldReflectionVector(data,normal) reflect (data.worldRefl, half3(dot(data.internalSurfaceTtoW0,normal), dot(data.internalSurfaceTtoW1,normal), dot(data.internalSurfaceTtoW2,normal)))
#define WorldNormalVector(data,normal) fixed3(dot(data.internalSurfaceTtoW0,normal), dot(data.internalSurfaceTtoW1,normal), dot(data.internalSurfaceTtoW2,normal))

#ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING
#endif
/* UNITY: Original start of shader */
        // Physically based Standard lighting model, and enable shadows on all light types
        //#pragma surface surf Standard fullforwardshadows vertex:vert

        // Use shader model 3.0 target, to get nicer looking lighting
        //#pragma target 5.0

        #include "UnityStandardUtils.cginc"

        sampler2D _MainTex;
        sampler2D _MetallicGlossMap;
        sampler2D _BumpMap;
        sampler2D _OcclusionMap;

        struct Input {
            float2 uv_MainTex;
            float3 worldNormal; INTERNAL_DATA
        };

        fixed4 _Color;
        half _GlossMapScale;
        half _BumpScale;
        half _OcclusionStrength;

        void surf (Input IN, inout SurfaceOutputStandard o) {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;

            fixed4 mg = tex2D(_MetallicGlossMap, IN.uv_MainTex);
            o.Metallic = mg.r;
            o.Smoothness = mg.a * _GlossMapScale;

            float3 vNormalWsDdx = ddx_fine( IN.worldNormal.xyz );
            float3 vNormalWsDdy = ddy_fine( IN.worldNormal.xyz );
            float flGeometricRoughnessFactor = pow( saturate( max( dot( vNormalWsDdx.xyz, vNormalWsDdx.xyz ), dot( vNormalWsDdy.xyz, vNormalWsDdy.xyz ) ) ), 0.333 );
            o.Smoothness = min( o.Smoothness, 1.0 - flGeometricRoughnessFactor ); // Ensure we don’t double-count roughness if normal map encodes geometric roughness

            half occ = tex2D(_OcclusionMap, IN.uv_MainTex).g;
            o.Occlusion = LerpOneTo (occ, _OcclusionStrength);

            half3 normal = UnpackScaleNormal(tex2D(_BumpMap, IN.uv_MainTex), _BumpScale);
            o.Normal = normal;
        }
      

// vertex-to-fragment interpolation data
// no lightmaps:
#ifndef LIGHTMAP_ON
struct v2f_surf {
    float4 pos : SV_POSITION;
    float2 pack0 : TEXCOORD0; // _MainTex
    float4 tSpace0 : TEXCOORD1;
    float4 tSpace1 : TEXCOORD2;
    float4 tSpace2 : TEXCOORD3;
    #if defined(_CENTROIDNORMAL)
    float3 centroidNormal : TEXCOORD4_centroid;
    #endif
    #if UNITY_SHOULD_SAMPLE_SH
    half3 sh : TEXCOORD5; // SH
    #endif
    UNITY_SHADOW_COORDS(6)
    UNITY_FOG_COORDS(7)
    #if SHADER_TARGET >= 30
    float4 lmap : TEXCOORD8;
    #endif
    UNITY_VERTEX_INPUT_INSTANCE_ID
    UNITY_VERTEX_OUTPUT_STEREO
};
#endif
// with lightmaps:
#ifdef LIGHTMAP_ON
struct v2f_surf {
    float4 pos : SV_POSITION;
    float2 pack0 : TEXCOORD0; // _MainTex
    float4 tSpace0 : TEXCOORD1;
    float4 tSpace1 : TEXCOORD2;
    float4 tSpace2 : TEXCOORD3;
    #if defined(_CENTROIDNORMAL)
    float3 centroidNormal : TEXCOORD4_centroid;
    #endif
    float4 lmap : TEXCOORD5;
    UNITY_SHADOW_COORDS(6)
    UNITY_FOG_COORDS(7)
    UNITY_VERTEX_INPUT_INSTANCE_ID
    UNITY_VERTEX_OUTPUT_STEREO
};
#endif
float4 _MainTex_ST;

// vertex shader
v2f_surf vert_surf (appdata_full v) {
    UNITY_SETUP_INSTANCE_ID(v);
    v2f_surf o;
    UNITY_INITIALIZE_OUTPUT(v2f_surf,o);
    UNITY_TRANSFER_INSTANCE_ID(v,o);
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
    o.pos = UnityObjectToClipPos(v.vertex);
    o.pack0.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
    float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
    float3 worldNormal = UnityObjectToWorldNormal(v.normal);
    float3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
    float tangentSign = v.tangent.w * unity_WorldTransformParams.w;
    float3 worldBinormal = cross(worldNormal, worldTangent) * tangentSign;
    o.tSpace0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
    o.tSpace1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
    o.tSpace2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);
    #if defined(_CENTROIDNORMAL)
    o.centroidNormal = worldNormal;
    #endif
    #ifdef DYNAMICLIGHTMAP_ON
    o.lmap.zw = v.texcoord2.xy * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
    #endif
    #ifdef LIGHTMAP_ON
    o.lmap.xy = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
    #endif

    // SH/ambient and vertex lights
    #ifndef LIGHTMAP_ON
        #if UNITY_SHOULD_SAMPLE_SH
            o.sh = 0;
            // Approximated illumination from non-important point lights
            #ifdef VERTEXLIGHT_ON
                o.sh += Shade4PointLights (
                    unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
                    unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,
                    unity_4LightAtten0, worldPos, worldNormal);
            #endif
            o.sh = ShadeSHPerVertex (worldNormal, o.sh);
        #endif
    #endif // !LIGHTMAP_ON

    UNITY_TRANSFER_SHADOW(o,v.texcoord1.xy); // pass shadow coordinates to pixel shader
    UNITY_TRANSFER_FOG(o,o.pos); // pass fog coordinates to pixel shader
    return o;
}

// fragment shader
fixed4 frag_surf (v2f_surf IN) : SV_Target {
    UNITY_SETUP_INSTANCE_ID(IN);
    // prepare and unpack data
    Input surfIN;
    UNITY_INITIALIZE_OUTPUT(Input,surfIN);
    surfIN.uv_MainTex.x = 1.0;
    surfIN.uv_MainTex = IN.pack0.xy;
    float3 worldPos = float3(IN.tSpace0.w, IN.tSpace1.w, IN.tSpace2.w);
    #ifndef USING_DIRECTIONAL_LIGHT
        fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));
    #else
        fixed3 lightDir = _WorldSpaceLightPos0.xyz;
    #endif
    fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
    surfIN.worldNormal = float3(IN.tSpace0.z, IN.tSpace1.z, IN.tSpace2.z);
    surfIN.internalSurfaceTtoW0 = IN.tSpace0.xyz;
    surfIN.internalSurfaceTtoW1 = IN.tSpace1.xyz;
    surfIN.internalSurfaceTtoW2 = IN.tSpace2.xyz;
    #ifdef UNITY_COMPILER_HLSL
    SurfaceOutputStandard o = (SurfaceOutputStandard)0;
    #else
    SurfaceOutputStandard o;
    #endif
    o.Albedo = 0.0;
    o.Emission = 0.0;
    o.Alpha = 0.0;
    o.Occlusion = 1.0;
    fixed3 normalWorldVertex = fixed3(0,0,1);

    #if defined(_CENTROIDNORMAL)
    if ( dot( surfIN.worldNormal, surfIN.worldNormal ) >= 1.01 )
    {
        IN.tSpace0.z = IN.centroidNormal.x;
        IN.tSpace1.z = IN.centroidNormal.y;
        IN.tSpace2.z = IN.centroidNormal.z;
    }
    #endif

    // call surface function
    surf (surfIN, o);

    // compute lighting & shadowing factor
    UNITY_LIGHT_ATTENUATION(atten, IN, worldPos)
    fixed4 c = 0;
    fixed3 worldN;
    worldN.x = dot(IN.tSpace0.xyz, o.Normal);
    worldN.y = dot(IN.tSpace1.xyz, o.Normal);
    worldN.z = dot(IN.tSpace2.xyz, o.Normal);
    o.Normal = normalize(worldN);

    // Setup lighting environment
    UnityGI gi;
    UNITY_INITIALIZE_OUTPUT(UnityGI, gi);
    gi.indirect.diffuse = 0;
    gi.indirect.specular = 0;
    gi.light.color = _LightColor0.rgb;
    gi.light.dir = lightDir;
    // Call GI (lightmaps/SH/reflections) lighting function
    UnityGIInput giInput;
    UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput);
    giInput.light = gi.light;
    giInput.worldPos = worldPos;
    giInput.worldViewDir = worldViewDir;
    giInput.atten = atten;
    #if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON)
        giInput.lightmapUV = IN.lmap;
    #else
        giInput.lightmapUV = 0.0;
    #endif
    #if UNITY_SHOULD_SAMPLE_SH
        giInput.ambient = IN.sh;
    #else
        giInput.ambient.rgb = 0.0;
    #endif
    giInput.probeHDR[0] = unity_SpecCube0_HDR;
    giInput.probeHDR[1] = unity_SpecCube1_HDR;
    #if defined(UNITY_SPECCUBE_BLENDING) || defined(UNITY_SPECCUBE_BOX_PROJECTION)
        giInput.boxMin[0] = unity_SpecCube0_BoxMin; // .w holds lerp value for blending
    #endif
    #ifdef UNITY_SPECCUBE_BOX_PROJECTION
        giInput.boxMax[0] = unity_SpecCube0_BoxMax;
        giInput.probePosition[0] = unity_SpecCube0_ProbePosition;
        giInput.boxMax[1] = unity_SpecCube1_BoxMax;
        giInput.boxMin[1] = unity_SpecCube1_BoxMin;
        giInput.probePosition[1] = unity_SpecCube1_ProbePosition;
    #endif
    LightingStandard_GI(o, giInput, gi);

    // realtime lighting: call lighting function
    c += LightingStandard (o, worldViewDir, gi);
    UNITY_APPLY_FOG(IN.fogCoord, c); // apply fog
    UNITY_OPAQUE_ALPHA(c.a);
    return c;
}


#endif


ENDCG

}

    // ---- forward rendering additive lights pass:
    Pass {
        Name "FORWARD"
        Tags { "LightMode" = "ForwardAdd" }
        ZWrite Off Blend One One

CGPROGRAM
// compile directives
#pragma vertex vert_surf
#pragma fragment frag_surf
#pragma target 5.0
#pragma multi_compile_instancing
#pragma shader_feature _CENTROIDNORMAL
#pragma multi_compile_fog
#pragma skip_variants INSTANCING_ON
#pragma multi_compile_fwdadd_fullshadows
#include "HLSLSupport.cginc"
#include "UnityShaderVariables.cginc"
#include "UnityShaderUtilities.cginc"
// -------- variant for: <when no other keywords are defined>
#if !defined(INSTANCING_ON)
// Surface shader code generated based on:
// vertex modifier: 'vert'
// writes to per-pixel normal: YES
// writes to emission: no
// writes to occlusion: YES
// needs world space reflection vector: no
// needs world space normal vector: YES
// needs screen space position: no
// needs world space position: no
// needs view direction: no
// needs world space view direction: no
// needs world space position for lighting: YES
// needs world space view direction for lighting: YES
// needs world space view direction for lightmaps: no
// needs vertex color: no
// needs VFACE: no
// passes tangent-to-world matrix to pixel shader: YES
// reads from normal: no
// 1 texcoords actually used
//   float2 _MainTex
#define UNITY_PASS_FORWARDADD
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "UnityPBSLighting.cginc"
#include "AutoLight.cginc"

#define INTERNAL_DATA half3 internalSurfaceTtoW0; half3 internalSurfaceTtoW1; half3 internalSurfaceTtoW2;
#define WorldReflectionVector(data,normal) reflect (data.worldRefl, half3(dot(data.internalSurfaceTtoW0,normal), dot(data.internalSurfaceTtoW1,normal), dot(data.internalSurfaceTtoW2,normal)))
#define WorldNormalVector(data,normal) fixed3(dot(data.internalSurfaceTtoW0,normal), dot(data.internalSurfaceTtoW1,normal), dot(data.internalSurfaceTtoW2,normal))

#ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING
#endif
/* UNITY: Original start of shader */
        // Physically based Standard lighting model, and enable shadows on all light types
        //#pragma surface surf Standard fullforwardshadows vertex:vert

        // Use shader model 3.0 target, to get nicer looking lighting
        //#pragma target 5.0

        #include "UnityStandardUtils.cginc"

        sampler2D _MainTex;
        sampler2D _MetallicGlossMap;
        sampler2D _BumpMap;
        sampler2D _OcclusionMap;

        struct Input {
            float2 uv_MainTex;
            float3 worldNormal; INTERNAL_DATA
        };

        fixed4 _Color;
        half _GlossMapScale;
        half _BumpScale;
        half _OcclusionStrength;

        void surf (Input IN, inout SurfaceOutputStandard o) {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;

            fixed4 mg = tex2D(_MetallicGlossMap, IN.uv_MainTex);
            o.Metallic = mg.r;
            o.Smoothness = mg.a * _GlossMapScale;

            float3 vNormalWsDdx = ddx_fine( IN.worldNormal.xyz );
            float3 vNormalWsDdy = ddy_fine( IN.worldNormal.xyz );
            float flGeometricRoughnessFactor = pow( saturate( max( dot( vNormalWsDdx.xyz, vNormalWsDdx.xyz ), dot( vNormalWsDdy.xyz, vNormalWsDdy.xyz ) ) ), 0.333 );
            o.Smoothness = min( o.Smoothness, 1.0 - flGeometricRoughnessFactor ); // Ensure we don’t double-count roughness if normal map encodes geometric roughness

            half occ = tex2D(_OcclusionMap, IN.uv_MainTex).g;
            o.Occlusion = LerpOneTo (occ, _OcclusionStrength);

            half3 normal = UnpackScaleNormal(tex2D(_BumpMap, IN.uv_MainTex), _BumpScale);
            o.Normal = normal;
        }
      

// vertex-to-fragment interpolation data
struct v2f_surf {
    float4 pos : SV_POSITION;
    float2 pack0 : TEXCOORD0; // _MainTex
    fixed3 tSpace0 : TEXCOORD1;
    fixed3 tSpace1 : TEXCOORD2;
    fixed3 tSpace2 : TEXCOORD3;
    float3 worldPos : TEXCOORD4;
    float3 centroidNormal : TEXCOORD5; // centroidNormal
    UNITY_SHADOW_COORDS(6)
    UNITY_FOG_COORDS(7)
    UNITY_VERTEX_INPUT_INSTANCE_ID
    UNITY_VERTEX_OUTPUT_STEREO
};
float4 _MainTex_ST;

// vertex shader
v2f_surf vert_surf (appdata_full v) {
    UNITY_SETUP_INSTANCE_ID(v);
    v2f_surf o;
    UNITY_INITIALIZE_OUTPUT(v2f_surf,o);
    UNITY_TRANSFER_INSTANCE_ID(v,o);
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
    o.pos = UnityObjectToClipPos(v.vertex);
    o.pack0.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
    float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
    float3 worldNormal = UnityObjectToWorldNormal(v.normal);
    float3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
    float tangentSign = v.tangent.w * unity_WorldTransformParams.w;
    float3 worldBinormal = cross(worldNormal, worldTangent) * tangentSign;
    o.tSpace0 = fixed3(worldTangent.x, worldBinormal.x, worldNormal.x);
    o.tSpace1 = fixed3(worldTangent.y, worldBinormal.y, worldNormal.y);
    o.tSpace2 = fixed3(worldTangent.z, worldBinormal.z, worldNormal.z);
    #if defined(_CENTROIDNORMAL)
    o.centroidNormal = worldNormal;
    #endif
    o.worldPos = worldPos;

    UNITY_TRANSFER_SHADOW(o,v.texcoord1.xy); // pass shadow coordinates to pixel shader
    UNITY_TRANSFER_FOG(o,o.pos); // pass fog coordinates to pixel shader
    return o;
}

// fragment shader
fixed4 frag_surf (v2f_surf IN) : SV_Target {
    UNITY_SETUP_INSTANCE_ID(IN);
    // prepare and unpack data
    Input surfIN;
    UNITY_INITIALIZE_OUTPUT(Input,surfIN);
    surfIN.uv_MainTex.x = 1.0;
    surfIN.uv_MainTex = IN.pack0.xy;
    float3 worldPos = IN.worldPos;
    #ifndef USING_DIRECTIONAL_LIGHT
        fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));
    #else
        fixed3 lightDir = _WorldSpaceLightPos0.xyz;
    #endif
    fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
    surfIN.worldNormal = float3(IN.tSpace0.z, IN.tSpace1.z, IN.tSpace2.z);
    surfIN.internalSurfaceTtoW0 = IN.tSpace0.xyz;
    surfIN.internalSurfaceTtoW1 = IN.tSpace1.xyz;
    surfIN.internalSurfaceTtoW2 = IN.tSpace2.xyz;
    #ifdef UNITY_COMPILER_HLSL
    SurfaceOutputStandard o = (SurfaceOutputStandard)0;
    #else
    SurfaceOutputStandard o;
    #endif
    o.Albedo = 0.0;
    o.Emission = 0.0;
    o.Alpha = 0.0;
    o.Occlusion = 1.0;
    fixed3 normalWorldVertex = fixed3(0,0,1);

    #if defined(_CENTROIDNORMAL)
    if ( dot( surfIN.worldNormal, surfIN.worldNormal ) >= 1.01 )
    {
        IN.tSpace0.z = IN.centroidNormal.x;
        IN.tSpace1.z = IN.centroidNormal.y;
        IN.tSpace2.z = IN.centroidNormal.z;
    }
    #endif

    // call surface function
    surf (surfIN, o);
    UNITY_LIGHT_ATTENUATION(atten, IN, worldPos)
    fixed4 c = 0;
    fixed3 worldN;
    worldN.x = dot(IN.tSpace0.xyz, o.Normal);
    worldN.y = dot(IN.tSpace1.xyz, o.Normal);
    worldN.z = dot(IN.tSpace2.xyz, o.Normal);
    o.Normal = normalize(worldN);

    // Setup lighting environment
    UnityGI gi;
    UNITY_INITIALIZE_OUTPUT(UnityGI, gi);
    gi.indirect.diffuse = 0;
    gi.indirect.specular = 0;
    gi.light.color = _LightColor0.rgb;
    gi.light.dir = lightDir;
    gi.light.color *= atten;
    c += LightingStandard (o, worldViewDir, gi);
    c.a = 0.0;
    UNITY_APPLY_FOG(IN.fogCoord, c); // apply fog
    UNITY_OPAQUE_ALPHA(c.a);
    return c;
}


#endif


ENDCG

}

    // ---- deferred shading pass:
    Pass {
        Name "DEFERRED"
        Tags { "LightMode" = "Deferred" }

CGPROGRAM
// compile directives
#pragma vertex vert_surf
#pragma fragment frag_surf
#pragma target 5.0
#pragma multi_compile_instancing
#pragma shader_feature _CENTROIDNORMAL
#pragma exclude_renderers nomrt
#pragma skip_variants FOG_LINEAR FOG_EXP FOG_EXP2
#pragma multi_compile_prepassfinal
#include "HLSLSupport.cginc"
#include "UnityShaderVariables.cginc"
#include "UnityShaderUtilities.cginc"
// -------- variant for: <when no other keywords are defined>
#if !defined(INSTANCING_ON)
// Surface shader code generated based on:
// vertex modifier: 'vert'
// writes to per-pixel normal: YES
// writes to emission: no
// writes to occlusion: YES
// needs world space reflection vector: no
// needs world space normal vector: YES
// needs screen space position: no
// needs world space position: no
// needs view direction: no
// needs world space view direction: no
// needs world space position for lighting: YES
// needs world space view direction for lighting: YES
// needs world space view direction for lightmaps: no
// needs vertex color: no
// needs VFACE: no
// passes tangent-to-world matrix to pixel shader: YES
// reads from normal: no
// 1 texcoords actually used
//   float2 _MainTex
#define UNITY_PASS_DEFERRED
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "UnityPBSLighting.cginc"

#define INTERNAL_DATA half3 internalSurfaceTtoW0; half3 internalSurfaceTtoW1; half3 internalSurfaceTtoW2;
#define WorldReflectionVector(data,normal) reflect (data.worldRefl, half3(dot(data.internalSurfaceTtoW0,normal), dot(data.internalSurfaceTtoW1,normal), dot(data.internalSurfaceTtoW2,normal)))
#define WorldNormalVector(data,normal) fixed3(dot(data.internalSurfaceTtoW0,normal), dot(data.internalSurfaceTtoW1,normal), dot(data.internalSurfaceTtoW2,normal))

#ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING
#endif
/* UNITY: Original start of shader */
        // Physically based Standard lighting model, and enable shadows on all light types
        //#pragma surface surf Standard fullforwardshadows vertex:vert

        // Use shader model 3.0 target, to get nicer looking lighting
        //#pragma target 5.0

        #include "UnityStandardUtils.cginc"

        sampler2D _MainTex;
        sampler2D _MetallicGlossMap;
        sampler2D _BumpMap;
        sampler2D _OcclusionMap;

        struct Input {
            float2 uv_MainTex;
            float3 worldNormal; INTERNAL_DATA
        };

        fixed4 _Color;
        half _GlossMapScale;
        half _BumpScale;
        half _OcclusionStrength;

        void surf (Input IN, inout SurfaceOutputStandard o) {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;

            fixed4 mg = tex2D(_MetallicGlossMap, IN.uv_MainTex);
            o.Metallic = mg.r;
            o.Smoothness = mg.a * _GlossMapScale;

            float3 vNormalWsDdx = ddx_fine( IN.worldNormal.xyz );
            float3 vNormalWsDdy = ddy_fine( IN.worldNormal.xyz );
            float flGeometricRoughnessFactor = pow( saturate( max( dot( vNormalWsDdx.xyz, vNormalWsDdx.xyz ), dot( vNormalWsDdy.xyz, vNormalWsDdy.xyz ) ) ), 0.333 );
            o.Smoothness = min( o.Smoothness, 1.0 - flGeometricRoughnessFactor ); // Ensure we don’t double-count roughness if normal map encodes geometric roughness

            half occ = tex2D(_OcclusionMap, IN.uv_MainTex).g;
            o.Occlusion = LerpOneTo (occ, _OcclusionStrength);

            half3 normal = UnpackScaleNormal(tex2D(_BumpMap, IN.uv_MainTex), _BumpScale);
            o.Normal = normal;
        }
      

// vertex-to-fragment interpolation data
struct v2f_surf {
    float4 pos : SV_POSITION;
    float2 pack0 : TEXCOORD0; // _MainTex
    float4 tSpace0 : TEXCOORD1;
    float4 tSpace1 : TEXCOORD2;
    float4 tSpace2 : TEXCOORD3;
    #if defined(_CENTROIDNORMAL)
    float3 centroidNormal : TEXCOORD4_centroid;
    #endif
#ifndef DIRLIGHTMAP_OFF
    half3 viewDir : TEXCOORD5;
#endif
    float4 lmap : TEXCOORD6;
#ifndef LIGHTMAP_ON
    #if UNITY_SHOULD_SAMPLE_SH
        half3 sh : TEXCOORD7; // SH
    #endif
#else
    #ifdef DIRLIGHTMAP_OFF
        float4 lmapFadePos : TEXCOORD7;
    #endif
#endif
    UNITY_VERTEX_INPUT_INSTANCE_ID
    UNITY_VERTEX_OUTPUT_STEREO
};
float4 _MainTex_ST;

// vertex shader
v2f_surf vert_surf (appdata_full v) {
    UNITY_SETUP_INSTANCE_ID(v);
    v2f_surf o;
    UNITY_INITIALIZE_OUTPUT(v2f_surf,o);
    UNITY_TRANSFER_INSTANCE_ID(v,o);
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
    o.pos = UnityObjectToClipPos(v.vertex);
    o.pack0.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
    float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
    float3 worldNormal = UnityObjectToWorldNormal(v.normal);
    float3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
    float tangentSign = v.tangent.w * unity_WorldTransformParams.w;
    float3 worldBinormal = cross(worldNormal, worldTangent) * tangentSign;
    o.tSpace0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
    o.tSpace1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
    o.tSpace2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);
    #if defined(_CENTROIDNORMAL)
    o.centroidNormal = worldNormal;
    #endif
    float3 viewDirForLight = UnityWorldSpaceViewDir(worldPos);
    #ifndef DIRLIGHTMAP_OFF
    o.viewDir.x = dot(viewDirForLight, worldTangent);
    o.viewDir.y = dot(viewDirForLight, worldBinormal);
    o.viewDir.z = dot(viewDirForLight, worldNormal);
    #endif
#ifdef DYNAMICLIGHTMAP_ON
    o.lmap.zw = v.texcoord2.xy * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
#else
    o.lmap.zw = 0;
#endif
#ifdef LIGHTMAP_ON
    o.lmap.xy = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
    #ifdef DIRLIGHTMAP_OFF
        o.lmapFadePos.xyz = (mul(unity_ObjectToWorld, v.vertex).xyz - unity_ShadowFadeCenterAndType.xyz) * unity_ShadowFadeCenterAndType.w;
        o.lmapFadePos.w = (-UnityObjectToViewPos(v.vertex).z) * (1.0 - unity_ShadowFadeCenterAndType.w);
    #endif
#else
    o.lmap.xy = 0;
        #if UNITY_SHOULD_SAMPLE_SH
            o.sh = 0;
            o.sh = ShadeSHPerVertex (worldNormal, o.sh);
        #endif
#endif
    return o;
}
#ifdef LIGHTMAP_ON
float4 unity_LightmapFade;
#endif
fixed4 unity_Ambient;

// fragment shader
void frag_surf (v2f_surf IN,
        out half4 outGBuffer0 : SV_Target0,
        out half4 outGBuffer1 : SV_Target1,
        out half4 outGBuffer2 : SV_Target2,
        out half4 outEmission : SV_Target3
#if defined(SHADOWS_SHADOWMASK) && (UNITY_ALLOWED_MRT_COUNT > 4)
        , out half4 outShadowMask : SV_Target4
#endif
) {
    UNITY_SETUP_INSTANCE_ID(IN);
    // prepare and unpack data
    Input surfIN;
    UNITY_INITIALIZE_OUTPUT(Input,surfIN);
    surfIN.uv_MainTex.x = 1.0;
    surfIN.uv_MainTex = IN.pack0.xy;
    float3 worldPos = float3(IN.tSpace0.w, IN.tSpace1.w, IN.tSpace2.w);
    #ifndef USING_DIRECTIONAL_LIGHT
        fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));
    #else
        fixed3 lightDir = _WorldSpaceLightPos0.xyz;
    #endif
    fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
    surfIN.worldNormal = float3(IN.tSpace0.z, IN.tSpace1.z, IN.tSpace2.z);
    surfIN.internalSurfaceTtoW0 = IN.tSpace0.xyz;
    surfIN.internalSurfaceTtoW1 = IN.tSpace1.xyz;
    surfIN.internalSurfaceTtoW2 = IN.tSpace2.xyz;
    #ifdef UNITY_COMPILER_HLSL
    SurfaceOutputStandard o = (SurfaceOutputStandard)0;
    #else
    SurfaceOutputStandard o;
    #endif
    o.Albedo = 0.0;
    o.Emission = 0.0;
    o.Alpha = 0.0;
    o.Occlusion = 1.0;
    fixed3 normalWorldVertex = fixed3(0,0,1);

    // call surface function
    surf (surfIN, o);
fixed3 originalNormal = o.Normal;
    fixed3 worldN;
    worldN.x = dot(IN.tSpace0.xyz, o.Normal);
    worldN.y = dot(IN.tSpace1.xyz, o.Normal);
    worldN.z = dot(IN.tSpace2.xyz, o.Normal);
    o.Normal = normalize(worldN);
    half atten = 1;

    // Setup lighting environment
    UnityGI gi;
    UNITY_INITIALIZE_OUTPUT(UnityGI, gi);
    gi.indirect.diffuse = 0;
    gi.indirect.specular = 0;
    gi.light.color = 0;
    gi.light.dir = half3(0,1,0);
    // Call GI (lightmaps/SH/reflections) lighting function
    UnityGIInput giInput;
    UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput);
    giInput.light = gi.light;
    giInput.worldPos = worldPos;
    giInput.worldViewDir = worldViewDir;
    giInput.atten = atten;
    #if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON)
        giInput.lightmapUV = IN.lmap;
    #else
        giInput.lightmapUV = 0.0;
    #endif
    #if UNITY_SHOULD_SAMPLE_SH
        giInput.ambient = IN.sh;
    #else
        giInput.ambient.rgb = 0.0;
    #endif
    giInput.probeHDR[0] = unity_SpecCube0_HDR;
    giInput.probeHDR[1] = unity_SpecCube1_HDR;
    #if defined(UNITY_SPECCUBE_BLENDING) || defined(UNITY_SPECCUBE_BOX_PROJECTION)
        giInput.boxMin[0] = unity_SpecCube0_BoxMin; // .w holds lerp value for blending
    #endif
    #ifdef UNITY_SPECCUBE_BOX_PROJECTION
        giInput.boxMax[0] = unity_SpecCube0_BoxMax;
        giInput.probePosition[0] = unity_SpecCube0_ProbePosition;
        giInput.boxMax[1] = unity_SpecCube1_BoxMax;
        giInput.boxMin[1] = unity_SpecCube1_BoxMin;
        giInput.probePosition[1] = unity_SpecCube1_ProbePosition;
    #endif
    LightingStandard_GI(o, giInput, gi);

    // call lighting function to output g-buffer
    outEmission = LightingStandard_Deferred (o, worldViewDir, gi, outGBuffer0, outGBuffer1, outGBuffer2);
    #if defined(SHADOWS_SHADOWMASK) && (UNITY_ALLOWED_MRT_COUNT > 4)
        outShadowMask = UnityGetRawBakedOcclusions (IN.lmap.xy, float3(0, 0, 0));
    #endif
    #ifndef UNITY_HDR_ON
    outEmission.rgb = exp2(-outEmission.rgb);
    #endif
}


#endif

// -------- variant for: INSTANCING_ON
#if defined(INSTANCING_ON)
// Surface shader code generated based on:
// vertex modifier: 'vert'
// writes to per-pixel normal: YES
// writes to emission: no
// writes to occlusion: YES
// needs world space reflection vector: no
// needs world space normal vector: YES
// needs screen space position: no
// needs world space position: no
// needs view direction: no
// needs world space view direction: no
// needs world space position for lighting: YES
// needs world space view direction for lighting: YES
// needs world space view direction for lightmaps: no
// needs vertex color: no
// needs VFACE: no
// passes tangent-to-world matrix to pixel shader: YES
// reads from normal: no
// 1 texcoords actually used
//   float2 _MainTex
#define UNITY_PASS_DEFERRED
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "UnityPBSLighting.cginc"

#define INTERNAL_DATA half3 internalSurfaceTtoW0; half3 internalSurfaceTtoW1; half3 internalSurfaceTtoW2;
#define WorldReflectionVector(data,normal) reflect (data.worldRefl, half3(dot(data.internalSurfaceTtoW0,normal), dot(data.internalSurfaceTtoW1,normal), dot(data.internalSurfaceTtoW2,normal)))
#define WorldNormalVector(data,normal) fixed3(dot(data.internalSurfaceTtoW0,normal), dot(data.internalSurfaceTtoW1,normal), dot(data.internalSurfaceTtoW2,normal))

#ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING
#endif
/* UNITY: Original start of shader */
        // Physically based Standard lighting model, and enable shadows on all light types
        //#pragma surface surf Standard fullforwardshadows vertex:vert

        // Use shader model 3.0 target, to get nicer looking lighting
        //#pragma target 5.0

        #include "UnityStandardUtils.cginc"

        sampler2D _MainTex;
        sampler2D _MetallicGlossMap;
        sampler2D _BumpMap;
        sampler2D _OcclusionMap;

        struct Input {
            float2 uv_MainTex;
            float3 worldNormal; INTERNAL_DATA
        };

        fixed4 _Color;
        half _GlossMapScale;
        half _BumpScale;
        half _OcclusionStrength;

        void surf (Input IN, inout SurfaceOutputStandard o) {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;

            fixed4 mg = tex2D(_MetallicGlossMap, IN.uv_MainTex);
            o.Metallic = mg.r;
            o.Smoothness = mg.a * _GlossMapScale;

            float3 vNormalWsDdx = ddx_fine( IN.worldNormal.xyz );
            float3 vNormalWsDdy = ddy_fine( IN.worldNormal.xyz );
            float flGeometricRoughnessFactor = pow( saturate( max( dot( vNormalWsDdx.xyz, vNormalWsDdx.xyz ), dot( vNormalWsDdy.xyz, vNormalWsDdy.xyz ) ) ), 0.333 );
            o.Smoothness = min( o.Smoothness, 1.0 - flGeometricRoughnessFactor ); // Ensure we don’t double-count roughness if normal map encodes geometric roughness

            half occ = tex2D(_OcclusionMap, IN.uv_MainTex).g;
            o.Occlusion = LerpOneTo (occ, _OcclusionStrength);

            half3 normal = UnpackScaleNormal(tex2D(_BumpMap, IN.uv_MainTex), _BumpScale);
            o.Normal = normal;
        }
      

// vertex-to-fragment interpolation data
struct v2f_surf {
    float4 pos : SV_POSITION;
    float2 pack0 : TEXCOORD0; // _MainTex
    float4 tSpace0 : TEXCOORD1;
    float4 tSpace1 : TEXCOORD2;
    float4 tSpace2 : TEXCOORD3;
    #if defined(_CENTROIDNORMAL)
    float3 centroidNormal : TEXCOORD4_centroid;
    #endif
#ifndef DIRLIGHTMAP_OFF
    half3 viewDir : TEXCOORD5;
#endif
    float4 lmap : TEXCOORD6;
#ifndef LIGHTMAP_ON
    #if UNITY_SHOULD_SAMPLE_SH
        half3 sh : TEXCOORD7; // SH
    #endif
#else
    #ifdef DIRLIGHTMAP_OFF
        float4 lmapFadePos : TEXCOORD7;
    #endif
#endif
    UNITY_VERTEX_INPUT_INSTANCE_ID
    UNITY_VERTEX_OUTPUT_STEREO
};
float4 _MainTex_ST;

// vertex shader
v2f_surf vert_surf (appdata_full v) {
    UNITY_SETUP_INSTANCE_ID(v);
    v2f_surf o;
    UNITY_INITIALIZE_OUTPUT(v2f_surf,o);
    UNITY_TRANSFER_INSTANCE_ID(v,o);
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
    o.pos = UnityObjectToClipPos(v.vertex);
    o.pack0.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
    float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
    float3 worldNormal = UnityObjectToWorldNormal(v.normal);
    float3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
    float tangentSign = v.tangent.w * unity_WorldTransformParams.w;
    float3 worldBinormal = cross(worldNormal, worldTangent) * tangentSign;
    o.tSpace0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
    o.tSpace1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
    o.tSpace2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);
    #if defined(_CENTROIDNORMAL)
    o.centroidNormal = worldNormal;
    #endif
    float3 viewDirForLight = UnityWorldSpaceViewDir(worldPos);
    #ifndef DIRLIGHTMAP_OFF
    o.viewDir.x = dot(viewDirForLight, worldTangent);
    o.viewDir.y = dot(viewDirForLight, worldBinormal);
    o.viewDir.z = dot(viewDirForLight, worldNormal);
    #endif
#ifdef DYNAMICLIGHTMAP_ON
    o.lmap.zw = v.texcoord2.xy * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
#else
    o.lmap.zw = 0;
#endif
#ifdef LIGHTMAP_ON
    o.lmap.xy = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
    #ifdef DIRLIGHTMAP_OFF
        o.lmapFadePos.xyz = (mul(unity_ObjectToWorld, v.vertex).xyz - unity_ShadowFadeCenterAndType.xyz) * unity_ShadowFadeCenterAndType.w;
        o.lmapFadePos.w = (-UnityObjectToViewPos(v.vertex).z) * (1.0 - unity_ShadowFadeCenterAndType.w);
    #endif
#else
    o.lmap.xy = 0;
        #if UNITY_SHOULD_SAMPLE_SH
            o.sh = 0;
            o.sh = ShadeSHPerVertex (worldNormal, o.sh);
        #endif
#endif
    return o;
}
#ifdef LIGHTMAP_ON
float4 unity_LightmapFade;
#endif
fixed4 unity_Ambient;

// fragment shader
void frag_surf (v2f_surf IN,
        out half4 outGBuffer0 : SV_Target0,
        out half4 outGBuffer1 : SV_Target1,
        out half4 outGBuffer2 : SV_Target2,
        out half4 outEmission : SV_Target3
#if defined(SHADOWS_SHADOWMASK) && (UNITY_ALLOWED_MRT_COUNT > 4)
        , out half4 outShadowMask : SV_Target4
#endif
) {
    UNITY_SETUP_INSTANCE_ID(IN);
    // prepare and unpack data
    Input surfIN;
    UNITY_INITIALIZE_OUTPUT(Input,surfIN);
    surfIN.uv_MainTex.x = 1.0;
    surfIN.uv_MainTex = IN.pack0.xy;
    float3 worldPos = float3(IN.tSpace0.w, IN.tSpace1.w, IN.tSpace2.w);
    #ifndef USING_DIRECTIONAL_LIGHT
        fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));
    #else
        fixed3 lightDir = _WorldSpaceLightPos0.xyz;
    #endif
    fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
    surfIN.worldNormal = float3(IN.tSpace0.z, IN.tSpace1.z, IN.tSpace2.z);
    surfIN.internalSurfaceTtoW0 = IN.tSpace0.xyz;
    surfIN.internalSurfaceTtoW1 = IN.tSpace1.xyz;
    surfIN.internalSurfaceTtoW2 = IN.tSpace2.xyz;
    #ifdef UNITY_COMPILER_HLSL
    SurfaceOutputStandard o = (SurfaceOutputStandard)0;
    #else
    SurfaceOutputStandard o;
    #endif
    o.Albedo = 0.0;
    o.Emission = 0.0;
    o.Alpha = 0.0;
    o.Occlusion = 1.0;
    fixed3 normalWorldVertex = fixed3(0,0,1);

    // call surface function
    surf (surfIN, o);
fixed3 originalNormal = o.Normal;
    fixed3 worldN;
    worldN.x = dot(IN.tSpace0.xyz, o.Normal);
    worldN.y = dot(IN.tSpace1.xyz, o.Normal);
    worldN.z = dot(IN.tSpace2.xyz, o.Normal);
    o.Normal = normalize(worldN);
    half atten = 1;

    // Setup lighting environment
    UnityGI gi;
    UNITY_INITIALIZE_OUTPUT(UnityGI, gi);
    gi.indirect.diffuse = 0;
    gi.indirect.specular = 0;
    gi.light.color = 0;
    gi.light.dir = half3(0,1,0);
    // Call GI (lightmaps/SH/reflections) lighting function
    UnityGIInput giInput;
    UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput);
    giInput.light = gi.light;
    giInput.worldPos = worldPos;
    giInput.worldViewDir = worldViewDir;
    giInput.atten = atten;
    #if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON)
        giInput.lightmapUV = IN.lmap;
    #else
        giInput.lightmapUV = 0.0;
    #endif
    #if UNITY_SHOULD_SAMPLE_SH
        giInput.ambient = IN.sh;
    #else
        giInput.ambient.rgb = 0.0;
    #endif
    giInput.probeHDR[0] = unity_SpecCube0_HDR;
    giInput.probeHDR[1] = unity_SpecCube1_HDR;
    #if defined(UNITY_SPECCUBE_BLENDING) || defined(UNITY_SPECCUBE_BOX_PROJECTION)
        giInput.boxMin[0] = unity_SpecCube0_BoxMin; // .w holds lerp value for blending
    #endif
    #ifdef UNITY_SPECCUBE_BOX_PROJECTION
        giInput.boxMax[0] = unity_SpecCube0_BoxMax;
        giInput.probePosition[0] = unity_SpecCube0_ProbePosition;
        giInput.boxMax[1] = unity_SpecCube1_BoxMax;
        giInput.boxMin[1] = unity_SpecCube1_BoxMin;
        giInput.probePosition[1] = unity_SpecCube1_ProbePosition;
    #endif
    LightingStandard_GI(o, giInput, gi);

    // call lighting function to output g-buffer
    outEmission = LightingStandard_Deferred (o, worldViewDir, gi, outGBuffer0, outGBuffer1, outGBuffer2);
    #if defined(SHADOWS_SHADOWMASK) && (UNITY_ALLOWED_MRT_COUNT > 4)
        outShadowMask = UnityGetRawBakedOcclusions (IN.lmap.xy, float3(0, 0, 0));
    #endif
    #ifndef UNITY_HDR_ON
    outEmission.rgb = exp2(-outEmission.rgb);
    #endif
}


#endif


ENDCG

}

    // ---- meta information extraction pass:
    Pass {
        Name "Meta"
        Tags { "LightMode" = "Meta" }
        Cull Off

CGPROGRAM
// compile directives
#pragma vertex vert_surf
#pragma fragment frag_surf
#pragma target 5.0
#pragma multi_compile_instancing
#pragma shader_feature _CENTROIDNORMAL
#pragma skip_variants FOG_LINEAR FOG_EXP FOG_EXP2
#pragma skip_variants INSTANCING_ON
#pragma shader_feature EDITOR_VISUALIZATION

#include "HLSLSupport.cginc"
#include "UnityShaderVariables.cginc"
#include "UnityShaderUtilities.cginc"
// -------- variant for: <when no other keywords are defined>
#if !defined(INSTANCING_ON)
// Surface shader code generated based on:
// vertex modifier: 'vert'
// writes to per-pixel normal: YES
// writes to emission: no
// writes to occlusion: YES
// needs world space reflection vector: no
// needs world space normal vector: no
// needs screen space position: no
// needs world space position: no
// needs view direction: no
// needs world space view direction: no
// needs world space position for lighting: YES
// needs world space view direction for lighting: YES
// needs world space view direction for lightmaps: no
// needs vertex color: no
// needs VFACE: no
// passes tangent-to-world matrix to pixel shader: YES
// reads from normal: no
// 1 texcoords actually used
//   float2 _MainTex
#define UNITY_PASS_META
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "UnityPBSLighting.cginc"

#define INTERNAL_DATA half3 internalSurfaceTtoW0; half3 internalSurfaceTtoW1; half3 internalSurfaceTtoW2;
#define WorldReflectionVector(data,normal) reflect (data.worldRefl, half3(dot(data.internalSurfaceTtoW0,normal), dot(data.internalSurfaceTtoW1,normal), dot(data.internalSurfaceTtoW2,normal)))
#define WorldNormalVector(data,normal) fixed3(dot(data.internalSurfaceTtoW0,normal), dot(data.internalSurfaceTtoW1,normal), dot(data.internalSurfaceTtoW2,normal))

#ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING
#endif
/* UNITY: Original start of shader */
        // Physically based Standard lighting model, and enable shadows on all light types
        //#pragma surface surf Standard fullforwardshadows vertex:vert

        // Use shader model 3.0 target, to get nicer looking lighting
        //#pragma target 5.0

        #include "UnityStandardUtils.cginc"

        sampler2D _MainTex;
        sampler2D _MetallicGlossMap;
        sampler2D _BumpMap;
        sampler2D _OcclusionMap;

        struct Input {
            float2 uv_MainTex;
            float3 worldNormal; INTERNAL_DATA
        };

        fixed4 _Color;
        half _GlossMapScale;
        half _BumpScale;
        half _OcclusionStrength;

        void surf (Input IN, inout SurfaceOutputStandard o) {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;

            fixed4 mg = tex2D(_MetallicGlossMap, IN.uv_MainTex);
            o.Metallic = mg.r;
            o.Smoothness = mg.a * _GlossMapScale;

            float3 vNormalWsDdx = ddx_fine( IN.worldNormal.xyz );
            float3 vNormalWsDdy = ddy_fine( IN.worldNormal.xyz );
            float flGeometricRoughnessFactor = pow( saturate( max( dot( vNormalWsDdx.xyz, vNormalWsDdx.xyz ), dot( vNormalWsDdy.xyz, vNormalWsDdy.xyz ) ) ), 0.333 );
            o.Smoothness = min( o.Smoothness, 1.0 - flGeometricRoughnessFactor ); // Ensure we don’t double-count roughness if normal map encodes geometric roughness

            half occ = tex2D(_OcclusionMap, IN.uv_MainTex).g;
            o.Occlusion = LerpOneTo (occ, _OcclusionStrength);

            half3 normal = UnpackScaleNormal(tex2D(_BumpMap, IN.uv_MainTex), _BumpScale);
            o.Normal = normal;
        }
      
#include "UnityMetaPass.cginc"

// vertex-to-fragment interpolation data
struct v2f_surf {
    float4 pos : SV_POSITION;
    float2 pack0 : TEXCOORD0; // _MainTex
    float4 tSpace0 : TEXCOORD1;
    float4 tSpace1 : TEXCOORD2;
    float4 tSpace2 : TEXCOORD3;
    #if defined(_CENTROIDNORMAL)
    float3 centroidNormal : TEXCOORD4_centroid;
    #endif
    UNITY_VERTEX_INPUT_INSTANCE_ID
    UNITY_VERTEX_OUTPUT_STEREO
};
float4 _MainTex_ST;

// vertex shader
v2f_surf vert_surf (appdata_full v) {
    UNITY_SETUP_INSTANCE_ID(v);
    v2f_surf o;
    UNITY_INITIALIZE_OUTPUT(v2f_surf,o);
    UNITY_TRANSFER_INSTANCE_ID(v,o);
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
    o.pos = UnityMetaVertexPosition(v.vertex, v.texcoord1.xy, v.texcoord2.xy, unity_LightmapST, unity_DynamicLightmapST);
    o.pack0.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
    float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
    float3 worldNormal = UnityObjectToWorldNormal(v.normal);
    float3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
    float tangentSign = v.tangent.w * unity_WorldTransformParams.w;
    float3 worldBinormal = cross(worldNormal, worldTangent) * tangentSign;
    o.tSpace0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
    o.tSpace1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
    o.tSpace2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);
    #if defined(_CENTROIDNORMAL)
    o.centroidNormal = worldNormal;
    #endif
    return o;
}

// fragment shader
fixed4 frag_surf (v2f_surf IN) : SV_Target {
    UNITY_SETUP_INSTANCE_ID(IN);
    // prepare and unpack data
    Input surfIN;
    UNITY_INITIALIZE_OUTPUT(Input,surfIN);
    surfIN.uv_MainTex.x = 1.0;
    surfIN.uv_MainTex = IN.pack0.xy;
    float3 worldPos = float3(IN.tSpace0.w, IN.tSpace1.w, IN.tSpace2.w);
    #ifndef USING_DIRECTIONAL_LIGHT
        fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));
    #else
        fixed3 lightDir = _WorldSpaceLightPos0.xyz;
    #endif
    #ifdef UNITY_COMPILER_HLSL
    SurfaceOutputStandard o = (SurfaceOutputStandard)0;
    #else
    SurfaceOutputStandard o;
    #endif
    o.Albedo = 0.0;
    o.Emission = 0.0;
    o.Alpha = 0.0;
    o.Occlusion = 1.0;
    fixed3 normalWorldVertex = fixed3(0,0,1);

    // call surface function
    surf (surfIN, o);
    UnityMetaInput metaIN;
    UNITY_INITIALIZE_OUTPUT(UnityMetaInput, metaIN);
    metaIN.Albedo = o.Albedo;
    metaIN.Emission = o.Emission;
    return UnityMetaFragment(metaIN);
}


#endif


ENDCG

}

    // ---- end of surface shader generated code

    }
    FallBack "Diffuse"
}
6 Likes

Wow, thanks bgolus! I’ll take a look at this and try to play with it a little. I really appreciate it.

A better model to use to compare. HDR is turned off on the camera. This is a random free car model I downloaded online. White, 100% metallic, 0.8 smoothness, no normal maps. All screenshots have 8x MSAA enabled.



Animating between the three. The centroid sampling is most effective on the very thin geometry in the vents, but also a little on the head lamp.
3218526--246609--vr-standard-car-comparison.gif

6 Likes

The shader makes a night and day difference, dude. I feel like you should sell it on the asset store, haha.
So I have a vague idea how to add an emission texture and color. Am I supposed to plug it into the first block or into every block that has any emission calculations?

I wouldn’t sell it in that form, too annoying to modify (like adding emission). My suggestion is make a very simple surface shader with emission and look at the code that’s generated and see where it’s different from the shader I posted.

Honestly emission is fairly straight forward. Sample texture, multiply by some value, add to final fragment shader output. If you need it to work with baked lighting that requires some custom scripting to modify settings on the material. The Standard Shader does this as part of its custom editor.

So basically take a simple shader with emission, click compile, then compare the code with the shader you provided?
I was searching the forums, and you also posted something about emission:

Properties {
_MainTex ("Albedo", 2D) = "white" {}
_EmissionMap ("Emission Map", 2D) = "black" {}
[HDR] _EmissionColor ("Emission Color", Color) = (0,0,0)
}

...

fixed4 albedo = tex2D(_MainTex, i.uv);

... do some lighting ...
half4 output = half4(albedo.rgb * lighting.rgb, albedo.a);

half4 emission = tex2D(_EmissionMap, i.uv) * _EmissionColor;
output.rgb += emission.rgb;

return output;

So I was thinking I incorporate that into the VRStandard shader. It looks like what I want (just the emission texture and HDR color parameter from the Unity Standard shader)

I meant a simple surface shader and click on the “show generated code” button, not compile. But honestly you can shove that vertex / fragment code into the forward base pass(es) and be done.

Lux 2.02 offers specular AA based on the work of square enix:

Ah, good to know Lux is offering this as well. Though a minor correction, they’re using the same specular filtering from Ready at Dawn’s The Order: 1886. It’s not from Square Enix. I believe it’s the same prefiltering that Alloy uses.

that is true as far as the texture pre filtering is concerned.
lux’s lighting function however uses square enix’s geometry specular AA filtering :slight_smile:

Ah, nice. I hadn’t seen that one before.

Wondering if anyone succeeded in integrating emissive channel into bgolus’ excellent ‘Standard VR Shader’ and would be willing to post it?

I’ve tried the suggestions listed here, without success, as I have no experience writing shaders.

this shader would look 100 times better than the default standard shader in VR, since the glossy edges were the thing that bothered me most, even on high AA. I knew there was something wrong with it. @bgolus do you know if this is already implemented in URP / HDRP ? Or if this is possible to achieve via shadergraph ?

HDRP Lit Shader has a geometry roughness as a check box, might even be on the HDRP Lit Master Node Shader Graph.

It is not implemented for URP. There’s no reason it couldn’t be implemented in Shader Graph though, either manually with nodes, or a Custom Function node that calls the GeometricNormalFiltering function, which appears to be in shared SRP shader code.
https://github.com/Unity-Technologies/ScriptableRenderPipeline/blob/ae8f3d48c878b0e013e3a475d1e86ceb1d6d3dae/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl

1 Like

I’ll give it a try next week.

both ways work. Might give a summary tomorrow, too late now.

alomst forgot, here is a shadergraph node setup of the same thing

6 Likes