搞定冯乐乐入门精要的第七章基础光照之没用的疑难杂症886
以下是基础光照的导图:逐顶点分支与逐像素一致,图略。逐三角形一般不适用于平滑几何体,因此很少用到,略。
在做基本光照模型时,不管是做哪个着色频率(逐顶点、逐像素)都有一个基本思路:
- 首先一定要理解基本光照模型的原理,也就是知道公式背后的含义
- 想清楚要做逐顶点还是逐像素,这意味着你会在vertexshader还是fragment shader中写光照计算的代码;意味着你要规定v2f结构体的数据内容。
- 想清楚为了计算光照需要什么变量(光照公式中有哪些变量),为了得到这些变量数据,需要做什么?有哪些是unity内置变量可以直接得到?意味着你要计算还是找内置变量/函数以及你要写上内置变量相应的cginc库
- 这些变量是在什么坐标空间(模型空间?世界空间?)下计算的?意味着你要做坐标变换。
- 套公式进去计算,return color。
下面是写代码时我遇到的问题:
问题1:逐片元光照时,在vertexshader中归一化了法向量,传入片元中直接使用为什么会出错呢?
计算光照要用归一化的向量,我就想,在顶点着色器归一化向量之后再传到片元着色器不是一样的吗?(现在:你脑子瓦特了,当然不一样啊。
这个问题在哪里呢,在于我脑子里没有很清晰的渲染管线的概念,顶点着色器中的法线是每个顶点都有的,而片元着色器中的法线是会在三角面片内部上进行插值,从而才能得到平滑的法线信息。所以传入的法线如果直接用了,哪怕之前归一化过,现在也并不是归一化的呀~当然会出错呀~
问题2:为什么将法线从模型空间变换到世界空间时与顶点的变换式子不同?
知识点太多专门写了一篇文章解释:涉及到法线的空间变换问题(法线是矢量而模型顶点是一个点,做变换时矢量需要舍弃平移的影响)。具体戳下面的文章
//为什么这个是右乘矩阵?为什么要(float3x3)
o.worldNormal = mul(v.normal,(float3x3)unity_WorldToObject);
o.worldVertexPos = mul(unity_ObjectToWorld, v.vertex).xyz;
问题3:虽然没有人在vertexshader中做纹理采样...但是我就是想试试,用tex2d采样时为什么不行?
搜到的答案说:
顶点纹理采样只在Shader Model 3中支持,并且无法使用tex2d()
函数。tex2D()
实际上是一个快捷方式,它指出“找出正确的mip级别来自动进行采样” – 在片段着色器中,这是使用隐式导数完成的,但是这些在顶点阶段不可用。所以,我们需要使用更明确的tex2dlod()
(它可以在顶点和片段阶段工作)。这个函数需要一个4分量vector,其x
和y
是uv空间中熟悉的纹理坐标, w
表示从哪个mip级别采样(0是可用的最高分辨率)。
然后我就用tex2dlod()采样,法线分辨率好低,是因为网格顶点数实在太少了~
以上我的问题就得到解决了,其实我的问题似乎也没什么意义,主要是对管线还不够亲密。但是对于自己“偏偏想试试这样做会怎么样”的想法,我觉得还挺有趣。所以就写文记录一下~希望自己以后也多问为什么。
附录代码
代码使用方法:在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
}
}
}