优化实现Mobile Diffuse动态直接光照shader

项目中美术使用了Unity自带的Mobile/Diffuse这个shader制作了一部分场景素材,这个shader会依赖场景中的动态实时光源,比较耗费。

于是自己手动重写一份,简化shader的消耗,但同时保持美术已经制作场景的效果。

Shader "Mobile/Diffuse"
{
    Properties
    {
        _MainTex ("Base (RGB)", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 150

        CGPROGRAM
        #pragma surface surf Lambert noforwardadd nolightmap noshadow novertexlights nodynlightmap nodirlightmap 

        sampler2D _MainTex;

        struct Input
        {
            float2 uv_MainTex;
        };

        void surf (Input IN, inout SurfaceOutput o) {
            fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        ENDCG
    }

    Fallback "Mobile/VertexLit"
}

我在原始shader上添加了一些编译选项用来关闭一些特性,但编译出来的shader还是有很多非必要的运算。

手动实现一份noforwardadd的(只有一个pass)版本:

Shader "James/Scene/Mesh Lighting"
{
    Properties
    {
        _MainTex ("Base (RGB)", 2D) = "white" {}
    }
    
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue"="Geometry" }
        LOD 200
        
        Pass
        {
            Tags { "LightMode"="ForwardBase" }
            // Lighting Off
            
            CGPROGRAM
            #pragma fragmentoption ARB_precision_hint_fastest
            
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase
            #pragma multi_compile_fog
            
            #include "UnityCG.cginc"

            float4 _LightColor0;

            uniform sampler2D _MainTex;
            uniform half4 _MainTex_ST;

            struct vertexIN_base
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 texcoord : TEXCOORD0;
            };
            
            struct v2f_base
            {
                float4 pos : SV_POSITION;
                fixed3 vertexLight : COLOR;
                half2  uv : TEXCOORD0;
                float3 normal : TEXCOORD1;
                float3 lightDir : TEXCOORD2;
                UNITY_FOG_COORDS(3)
            };

            v2f_base vert(vertexIN_base v)
            {
                v2f_base o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                o.normal = v.normal;
                o.lightDir = ObjSpaceLightDir(v.vertex);

                half3 worldNormal = UnityObjectToWorldNormal(v.normal);
                float3 shlight = ShadeSH9(float4(worldNormal, 1.0));
                o.vertexLight = shlight;
                #ifdef VERTEXLIGHT_ON
                    o.vertexLight += 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

                UNITY_TRANSFER_FOG(o,o.pos);
                return o; 
            }
            
            fixed4 frag(v2f_base i) : COLOR
            {
                i.lightDir = normalize(i.lightDir);
                i.normal = normalize(i.normal);

                float diffuse = max(0, dot(i.normal, i.lightDir));

                fixed4 mainColor = tex2D(_MainTex, i.uv);
                fixed4 clr = mainColor * _LightColor0 * diffuse;
                clr.rgb += mainColor.rgb * i.vertexLight;

                UNITY_APPLY_FOG(i.fogCoord,clr);

                return clr;
            }
            ENDCG
        }
    }
    FallBack Off
}

上述shader和Mobile Diffuse效果基本一致(场景中光照并不复杂),并且默认的效果也是不带forward add的。

但这个shader还是依赖了场景中的实施光源数据。

于是乎,进一步,将场景中的实时光源全部移除,并将光源的颜色和方向信息直接写在shader的属性中,得到了下面的去光源版本:

Shader "James/Scene/Mesh Diffuse"
{
    Properties
    {
        _MainTex ("Base (RGB)", 2D) = "white" {}

        _MainLightColor("主光颜色", Color) = (1,1,1,1)
        _MainLightDir("主光方向", Vector) = (1,1,0,0)

        _SecondLightColor("辅光颜色", Color) = (1,1,1,1)
        _SecondLightDir("辅光方向", Vector) = (1,1,0,0)
        _SecondLightBrightness ("辅光强度", Range(0, 10)) = 0.9
    }
    
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue"="Geometry" }
        LOD 200
        
        Pass
        {
            Tags { "LightMode"="ForwardBase" }
            Lighting Off
            
            CGPROGRAM
            #pragma fragmentoption ARB_precision_hint_fastest
            
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fog
            
            #include "UnityCG.cginc"

            float4 _MainLightColor;
            float4 _MainLightDir;
            float4 _SecondLightColor;
            float4 _SecondLightDir;
            float _SecondLightBrightness;

            uniform sampler2D _MainTex;
            uniform half4 _MainTex_ST;

            struct vertexIN_base
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 texcoord : TEXCOORD0;
            };
            
            struct v2f_base
            {
                float4 pos : SV_POSITION;
                half2  uv : TEXCOORD0;
                float3 normal : TEXCOORD1;
                float3 lightDir : TEXCOORD2;
                float3 lightDir2 : TEXCOORD3;
                UNITY_FOG_COORDS(4)
            };

            v2f_base vert(vertexIN_base v)
            {
                v2f_base o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                o.normal = v.normal;

                o.lightDir = mul(unity_WorldToObject, _MainLightDir).xyz;
                o.lightDir2 = mul(unity_WorldToObject, _SecondLightDir).xyz;

                UNITY_TRANSFER_FOG(o,o.pos);
                return o; 
            }
            
            fixed4 frag(v2f_base i) : COLOR
            {
                i.lightDir = normalize(i.lightDir);
                i.lightDir2 = normalize(i.lightDir2);
                i.normal = normalize(i.normal);

                float diffuse = max(0, dot(i.normal, i.lightDir));
                float diffuse2 = max(0, dot(i.normal, i.lightDir2));

                fixed4 mainColor = tex2D(_MainTex, i.uv);
                fixed4 clr = mainColor * _MainLightColor * diffuse;
                clr += _SecondLightBrightness * (mainColor * _SecondLightColor * diffuse2);

                UNITY_APPLY_FOG(i.fogCoord,clr);

                return clr;
            }
            ENDCG
        }
    }
    FallBack Off
}

  这个版本支持两个光源信息,然后按照同样的Lambert光照方式来计算光照信息,其中辅光给了一个强度的调节因子。

  注:这里没有完全按照默认的计算方式来计算主光以外的光照信息,是因为half3 ShadeSH9 (half4 normal)所以来的unity_SHAr unity_SHAg unity_SHAb的计算方式不明确。

    // normal should be normalized, w=1.0
    half3 SHEvalLinearL0L1 (half4 normal)
    {
        half3 x;

        // Linear (L1) + constant (L0) polynomial terms
        x.r = dot(unity_SHAr,normal);
        x.g = dot(unity_SHAg,normal);
        x.b = dot(unity_SHAb,normal);

        return x;
    }

    // normal should be normalized, w=1.0
    half3 SHEvalLinearL2 (half4 normal)
    {
        half3 x1, x2;
        // 4 of the quadratic (L2) polynomials
        half4 vB = normal.xyzz * normal.yzzx;
        x1.r = dot(unity_SHBr,vB);
        x1.g = dot(unity_SHBg,vB);
        x1.b = dot(unity_SHBb,vB);

        // Final (5th) quadratic (L2) polynomial
        half vC = normal.x*normal.x - normal.y*normal.y;
        x2 = unity_SHC.rgb * vC;

        return x1 + x2;
    }

    // normal should be normalized, w=1.0
    // output in active color space
    half3 ShadeSH9 (half4 normal)
    {
        // Linear + constant polynomial terms
        half3 res = SHEvalLinearL0L1 (normal);

        // Quadratic polynomials
        res += SHEvalLinearL2 (normal);

    #   ifdef UNITY_COLORSPACE_GAMMA
            res = LinearToGammaSpace (res);
    #   endif

        return res;
    }

  Unity是怎么计算出unity_SHAr unity_SHAg  unity_SHAb这几个变量的,又知道的小伙伴可以告知一下哈。 

  下面是基于以上三个shader的实时渲染效果,其中"James/Scene/Mesh Diffuse"不依赖场景光源。

  

  让美术直接在材质球上面手动输入light dir,其实非常的不直观,于是写了一个工具,直接把场景中的lighting对应的shader中的方向值给打印出来:

    private void WriteLightingDir()
    {
        Object obj = Selection.activeGameObject;
        if (obj is GameObject == false) return;
        Debug.Log(-(obj as GameObject).transform.forward);
    }

  选中场景中的Lighting对象,然后执行上面的编辑期代码即可打印出方向值。

   添加环境光:

  如果只有场景光照,模型会有一些暗角,因此还需要加上自定义环境光的光照亮度。

  首先通过代码设置全局的环境光,方便shader访问:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[ExecuteInEditMode]
public class GlobalShaderSetting : MonoBehaviour
{
    public Color GlobalAmbientColor = new Color(0.5f, 0.5f, 0.5f, 1f);
    public float GlobalAmbientBrightness = 1;
    public Vector4 GlobalWindDirection = new Vector4(0.5f, 0.5f, 0.5f, 1f);

    [ContextMenu("Set")]
    private void Awake()
    {
        Shader.SetGlobalColor("_GlobalAmbientColor", GlobalAmbientColor);
        Shader.SetGlobalFloat("_GlobalAmbientBrightness", GlobalAmbientBrightness);
        Shader.SetGlobalVector("_GlobalWindDirection", GlobalWindDirection);
    }
}

  然后添加一个cginc的通用文件:

#ifndef JAMES_LIGHTING_H
#define JAMES_LIGHTING_H

float4 _GlobalAmbientColor;
float _GlobalAmbientBrightness;
float4 _GlobalWindDirection;

#define AMBIENT_COLOR _GlobalAmbientColor * _GlobalAmbientBrightness

#endif

  在需要添加环境光的shader中添加环境光的影响即可:

clr += mainTex * AMBIENT_COLOR;

 

 

  

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Sure, I can help you with that! A lighting model shader calculates the color of a pixel based on three types of light sources: ambient, diffuse, and specular. Here's a basic example of a lighting model shader, written in GLSL: ``` // Vertex shader attribute vec3 a_position; attribute vec3 a_normal; varying vec3 v_normal; uniform mat4 u_modelViewProjectionMatrix; uniform mat4 u_modelViewMatrix; void main() { gl_Position = u_modelViewProjectionMatrix * vec4(a_position, 1.0); v_normal = mat3(u_modelViewMatrix) * a_normal; } // Fragment shader precision mediump float; varying vec3 v_normal; uniform vec3 u_diffuseColor; uniform vec3 u_lightPosition; uniform vec3 u_ambientColor; uniform vec3 u_specularColor; uniform float u_shininess; void main() { vec3 N = normalize(v_normal); vec3 L = normalize(u_lightPosition - gl_FragCoord.xyz); vec3 V = normalize(-gl_FragCoord.xyz); vec3 H = normalize(L + V); vec3 diffuse = max(dot(N, L), 0.0) * u_diffuseColor; vec3 ambient = u_ambientColor; vec3 specular = pow(max(dot(N, H), 0.0), u_shininess) * u_specularColor; gl_FragColor = vec4(diffuse + ambient + specular, 1.0); } ``` This shader takes in several uniform variables, including the diffuse color of the object being shaded, the position of the light source, the ambient color of the scene, the specular color of the object being shaded, and the shininess of the object. The vertex shader calculates the normal vector for each vertex of the object, while the fragment shader calculates the diffuse, ambient, and specular lighting components for each pixel. I hope this helps you get started with writing your own lighting model shader!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值