《Unity Shader 入门精要》MyProject: letsStartShader笔记 02

0. Start with my nonsense

        There are some differences between "Lighting" in Unity and painting in Photoshop, at least in my mind. However, I'm glad to accept these differences, and I think it's the charm of learning art and technology at the same time!

        Today I'll talk something about illumination model.

1. Per-vertex or Per-pixel?

        An image of 1920x1080 contains 2073600 pixels, while it's almost impossible to find a model with so many vertices.

        Per-vertex lighting can save resources,  but it's too discrete and linear. When you meet something like pow(lightRange, gloss), it will be distorted. Per-pixel lighting is more like painting in Photoshop, accordingly it needs more resources.

        Their codes are very similar, in this passage I'll use per-pixel lighting.

2. What do we need to light a 3D objects?

        At this stage, we only consider simple Ambient, Diffuse and Specular reflection. "Simple" means there is only a single capsule, no occlusion, no chamfer, no metallic and so on.

i. Diffuse

        To get how diffuse contributes to color, you need to get:

         _LightColor0 —— by #include "Lighting.cginc"

        _Diffuse —— by Properties

        dot(worldNormal, worldLightDir)

ii. Specular reflection

        To get how specular reflection contributes to color, you need to get:

         _LightColor0 —— by #include "Lighting.cginc"

        _Specular, _Gloss —— by Properties

        dot(reflectDir, viewDir)

Let's Coding!

Phong illumination model

        Start with a simple framework:

Shader "Phong"{
    Properties{
        _Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
        _Specular ("Specular", Color) = (1, 1, 1, 1)
        _Gloss ("Gloss", Range(8.0, 256)) = 20
    }
    SubShader{

        Pass{
            
            Tags { "LightMode"="ForwardBase" }

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            #include "Lighting.cginc"

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

            float4 vert(){

            }

            float4 frag(){

            }

            ENDCG
        }
    }
    
    Fallback "Specular"
}

How the struct works? 

        a2v gets every vertex and normal in model space.

        v2f gives clip space the vertex, as it saves every position and normal in world space. For normal transformations, use inverse transpose matrix.

* If it's per-vertex lighting, you need to calculate COLOR in vert() and save COLOR in v2f instead.

            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1; //No more affine transformations
            };

            v2f vert (a2v v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);

                o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;

                return o;
            }

How fragment shader works?

        Use worldNormal and worldLightDir to calculate the light and shade value of Diffuse.

        Use reflect(). When inputting worldLightDir, you need to reverse it, as it is originally used to describe the direction of objects to light. Then use reflectDir and viewDir to calculate Specular.

            fixed4 frag (v2f i) : SV_Target
            {
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);

                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));

                // Phong
                fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);

                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);

                return fixed4(ambient + diffuse + specular, 1.0);
            }

Blinn-Phong illumination model

        The way to calculate Specular Reflection has changed.

                // Blinn-Phong
                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
                fixed3 halfDir = normalize(worldLightDir + viewDir);

                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);

FINAL CODE (Phong)

Shader "Unlit/SpecularVertexLevel"
{
    Properties
    {
        _Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
        _Specular ("Specular", Color) = (1, 1, 1, 1)
        _Gloss ("Gloss", Range(8.0, 256)) = 20
    }
    SubShader
    {
        Pass
        {
            Tags { "LightMode"="ForwardBase" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Lighting.cginc"

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
            };

            v2f vert (a2v v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);

                o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;

                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);

                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));

                fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);

                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);

                return fixed4(ambient + diffuse + specular, 1.0);
            }
            ENDCG
        }
    }

    Fallback "Specular"
}

Outside the capsule

        In this passage, you set the _Diffuse color as pure color. But if we add a texture, the Diffuse color will be more colorful!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值