sv信道模型是什么_Unity Shader|基本光照模型实现中遇到的疑思

1efb3f411e5482f22a6adae765b41964.png

搞定冯乐乐入门精要的第七章基础光照之没用的疑难杂症886

以下是基础光照的导图:逐顶点分支与逐像素一致,图略。逐三角形一般不适用于平滑几何体,因此很少用到,略。

f8ce5523f8adc1142f4c79aece7e328a.png

在做基本光照模型时,不管是做哪个着色频率(逐顶点、逐像素)都有一个基本思路:

a1f42785be7856a91a330169d8f63f16.png
  1. 首先一定要理解基本光照模型的原理,也就是知道公式背后的含义
  2. 想清楚要做逐顶点还是逐像素,这意味着你会在vertexshader还是fragment shader中写光照计算的代码;意味着你要规定v2f结构体的数据内容。
  3. 想清楚为了计算光照需要什么变量(光照公式中有哪些变量),为了得到这些变量数据,需要做什么?有哪些是unity内置变量可以直接得到?意味着你要计算还是找内置变量/函数以及你要写上内置变量相应的cginc库
  4. 这些变量是在什么坐标空间(模型空间?世界空间?)下计算的?意味着你要做坐标变换。
  5. 套公式进去计算,return color

下面是写代码时我遇到的问题:

问题1:逐片元光照时,在vertexshader中归一化了法向量,传入片元中直接使用为什么会出错呢?

计算光照要用归一化的向量,我就想,在顶点着色器归一化向量之后再传到片元着色器不是一样的吗?(现在:你脑子瓦特了,当然不一样啊。

这个问题在哪里呢,在于我脑子里没有很清晰的渲染管线的概念,顶点着色器中的法线是每个顶点都有的,而片元着色器中的法线是会在三角面片内部上进行插值,从而才能得到平滑的法线信息。所以传入的法线如果直接用了,哪怕之前归一化过,现在也并不是归一化的呀~当然会出错呀~

问题2:为什么将法线从模型空间变换到世界空间时与顶点的变换式子不同?

知识点太多专门写了一篇文章解释:涉及到法线的空间变换问题(法线是矢量而模型顶点是一个点,做变换时矢量需要舍弃平移的影响)。具体戳下面的文章

//为什么这个是右乘矩阵?为什么要(float3x3)
o.worldNormal =  mul(v.normal,(float3x3)unity_WorldToObject);
o.worldVertexPos = mul(unity_ObjectToWorld, v.vertex).xyz;
aganztracy:图形学 | Shader |用一篇文章理解法线变换、切线空间、法线贴图​zhuanlan.zhihu.com
d38bf97d70cc5a5c8438e2922d7ace41.png

问题3:虽然没有人在vertexshader中做纹理采样...但是我就是想试试,用tex2d采样时为什么不行?

搜到的答案说:

顶点纹理采样只在Shader Model 3中支持,并且无法使用tex2d()函数tex2D()实际上是一个快捷方式,它指出“找出正确的mip级别来自动进行采样” – 在片段着色器中,这是使用隐式导数完成的,但是这些在顶点阶段不可用。所以,我们需要使用更明确的tex2dlod()(它可以在顶点和片段阶段工作)。这个函数需要一个4分量vector,其xy是uv空间中熟悉的纹理坐标, w表示从哪个mip级别采样(0是可用的最高分辨率)。

Why can't I sample a texture in a vertex shader?​gamedev.stackexchange.com
94177c3ec4f097a75bef232917408346.png

然后我就用tex2dlod()采样,法线分辨率好低,是因为网格顶点数实在太少了~

8a011500a743dcb95f1c609fe70d7b14.png
左图顶点数目较多,因此纹理更清晰

d3905bd6fc7301bc5db63a7d6a04c51b.png
正方体只有8个顶点,基本采样成一个颜色了

以上我的问题就得到解决了,其实我的问题似乎也没什么意义,主要是对管线还不够亲密。但是对于自己“偏偏想试试这样做会怎么样”的想法,我觉得还挺有趣。所以就写文记录一下~希望自己以后也多问为什么。

附录代码

代码使用方法:在vertexshader和fragmentshader中只能同时打开同一个序号进行计算,如下面都打开了代码段【1】的内容进行逐片元光照。

  • 【1】逐片元光照(逐像素光照)
  • 【2】内置函数的逐片元光照(逐像素光照)
  • 【3】逐顶点光照
//因为当前渲染路径是前向渲染的第一个pass: ForwardBase。只有平行光,平行光是没有光照衰减的
Shader "ZTQShaders/blinnPhongShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Diffuse ("Diffuse",Color) = (1, 1, 1, 1)
        _Specular ("Specular",Color ) = (1, 1, 1, 1)
        _Gloss ("Gloss",Range(1.0,256)) = 20
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            struct a2vb
            {
                float4 vertex : POSITION;//必做步骤
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;//必做步骤
                //
                fixed3 color :COLOR;
                float3 worldNormal :TEXCOORD1;
                float3 worldVertexPos :TEXCOORD2;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

            


            v2f vert (a2v v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);//模型空间到齐次剪裁空间(vertex shader 必做步骤,尽管代码中并未使用,也要做变换,因为管线之后的操作都需要用到)
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);//自动使用了纹理对应的ST变量对纹理做偏移和缩放变换
                // o.uv = v.uv.xy*_MainTex_ST.xy+_MainTex_ST.zw; //效果等上

                ///【1】【2】逐片元光照
                o.worldNormal =  mul(v.normal,(float3x3)unity_WorldToObject);//为什么这个是右乘矩阵?为什么要(float3x3)
                o.worldVertexPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                /逐片元光照



                【3】逐顶点光照
                以下所有向量都是在world space 中,仅此使用之前1要转换到世界坐标系 2要归一化
                // fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                // fixed3 normalDir = normalize(mul(v.normal,(float3x3)unity_WorldToObject));//为什么这个是右乘矩阵?为什么要(float3x3)
                // fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz -  mul(unity_ObjectToWorld, v.vertex).xyz);//而这个是左乘矩阵?
                // //

                // fixed3 albedo =  tex2Dlod(_MainTex,fixed4(o.uv,0,0)) *_Diffuse.rgb ;//顶点着色器中无法用tex2d 对纹理进行采样,此处用tex2Dlod
                // fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
                // //
                // fixed3 diff = _LightColor0.rgb * albedo.rgb * saturate(dot(normalDir,lightDir));
                // //
                // fixed h_scl= 0.5;
                // fixed h_bia = 0.5;
                // fixed3 diff_half  =_LightColor0.rgb * albedo.rgb * (h_scl*dot(normalDir,lightDir)+h_bia);
                // //
                // fixed3 reflectDir = normalize(reflect(-lightDir,normalDir));
                // fixed3 spec =  _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir,viewDir)),_Gloss);
                // //
                // fixed3 halfDir = normalize(lightDir + viewDir); //half vector
                // fixed3 spec_half = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(normalDir,halfDir)),_Gloss);
                // //
                // o.color = ambient + diff + spec_half;
                逐顶点光照

                return o;
                
            }

            fixed4 frag (v2f i) : SV_Target
            {

                //【1】逐片元光照(逐像素光照)
                以下所有向量都是在world space 中,仅此使用之前1要转换到世界坐标系 2要归一化
                fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                fixed3 normalDir =normalize(i.worldNormal);//??为什么一定要在这里normalize才正常?
                // fixed3 normalDir =i.worldNormal;//??为什么一定要在这里normalize才正常?
                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz -  i.worldVertexPos);//而这个是左乘矩阵?
                //
                fixed3 albedo = tex2D(_MainTex, i.uv) * _Diffuse.rgb ;
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
                //
                fixed3 diff = _LightColor0.rgb * albedo.rgb * saturate(dot(normalDir,lightDir));
                //
                fixed h_scl= 0.5;
                fixed h_bia = 0.5;
                fixed3 diff_half  =_LightColor0.rgb * albedo.rgb * (h_scl*dot(normalDir,lightDir)+h_bia);
                //
                fixed3 reflectDir = normalize(reflect(-lightDir,normalDir));
                fixed3 spec =  _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir,viewDir)),_Gloss);
                //
                fixed3 halfDir = normalize(lightDir + viewDir); //half vector
                fixed3 spec_half = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(normalDir,halfDir)),_Gloss);
                //
                fixed4 col = fixed4(ambient + diff + spec_half,1.0);
                // col =fixed4(normalDir,1); //fortest
                // col =fixed4(i.worldNormal,1);//fortest
                //逐片元光照(逐像素光照)

                //【2】内置函数的逐片元光照(逐像素光照)
                // fixed3 worldNormal = normalize(i.worldNormal);
                // fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldVertexPos));//内置函数计算lightdirection
                // fixed3 albedo = tex2D(_MainTex, i.uv) * _Diffuse.rgb ;
                // fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
                // fixed3 diff = _LightColor0.rgb * albedo.rgb * max(0, dot(worldNormal, worldLightDir));
                // fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldVertexPos));//内置函数计算viewdirection
                // fixed3 halfDir = normalize(worldLightDir + viewDir);
                // fixed3 spec = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
                // fixed4 col= fixed4(ambient + diff + spec, 1.0);
                内置函数的逐片元光照(逐像素光照)

                //【3】逐顶点光照
                // fixed4 col = fixed4(i.color,1);// sample the texture
                //逐顶点光照

                return  col;
            }
            ENDCG
        }
    }
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值