Shader学习的基础知识(三)基础光照
一直以来学程序除做出什么效果以外,大多数时候还是觉得程序挺枯燥的。
之前为了面试游戏公司在家苦学了Lua一个多月,结果一些很简单的题都答不上来。
很多人和我说Lua一个月能学完,而Shader一个月只能学些皮毛,但可能本身对做效果特别感兴趣,学起Shader来如有神助,兴奋点特别多。个人觉得进步也特别快…下面是个人觉得必需要记下来的点了,已经是经过极度压缩的,请一个字一个字慢慢的研读清楚,如有疑问和错误请给我留言,谢谢。
辐照度
在光学里,我们使用辐照度来量化光。
吸收和散射
散射只改变光的方向,但不改变光的密度和颜色。而吸收只改变光线的密度和颜色,但不改变光线的方向。
名词
高光反射表示物体表面是如何反射光线的,而漫反射部分则表示有多少光线会被折射、吸收和散射出表面。出射度是指光源上每单位面积向半个空间内发出的光通量。
着色
是指根据材质属性(如漫反射属性等)、光源信息(如光源方向、辐照度等),使用一个等式去沿某个观察射出度的过程叫着色。也叫照光照模型。
计算机图形学第一定律
如果它看起来是对的,那么它就是对的。
标准光照模型的4个部分
环境光用于描述其他所有的间接光照。
自发光这个用于描述当给定一个方向 时,一个身会向该方向发射多少辐射度。
漫反射 漫反射中视角位置不重要,因为反射完全是随机的,它符合兰伯特定律。
漫反射光照=(漫反射颜色*灯光颜色)Max(0,点乘(顶点表面单位法向量,光源位置单位向量))
max用是取正值,点乘部分的含义是:法向量与光源投影(即越相同)数值越大。
如果使用半兰伯特模型的话公式如下:
漫反射光照=(漫反射颜色灯光颜色)*Max(0,点乘(顶点表面单位法向量,光源位置单位向量)*0.5+0.5)
目的是让阴暗面内容更丰富。
高光反射 它是完全 符合真实世界的高光反射。公式如下:
光泽度越大亮点就越小
反射公式=高光反射颜色光源颜色Max(0,点乘(单位射出角度,单位摄像机指向顶点位置))
(公式解释:即顶点射出角度与顶点指身摄像机越像,投影值就越高就越亮)
或
中线(h)=顶点至向摄像机的向量(v) 顶点指向光i源的向量(l) 这两个值平增均值再归一化
反射公式=高光反射颜色光源颜色Max(0,点乘(单位法向量,中线))
(中线越接近法线说明射的角度越正,中线与法线的点乘值就越大,就越亮)
漫反射例子(非常经典,静下心来查看每一个字)
逐顶点光照模型 逐顶点计算量小,但如顶点数量不够,效果会不好。
Shader "Custom/TestShader6" { Properties{ _Diffuse("Diffuse",Color)=(1,1,1,1) } SubShader{ Pass{ //定义了这个才能得到一些内置的光照变量 Tags{"LightMode"="ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag #include "Lighting.cginc" fixed4 _Diffuse; //Appction To Vertex struct a2v{ float4 vertex:POSITION; float3 normal:NORMAL; }; //Vertex To Fragment struct v2f{ float4 pos:SV_POSITION; fixed3 color:COLOR; }; v2f vert(a2v v){ v2f o; //把顶点从模型坐标放在齐次坐标中 o.pos=UnityObjectToClipPos(v.vertex); //取得环境光部分 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; //把模型上的法线放在世界坐标空间中 //为什么是乘以WorldToObject呢,因为是ObjectToWorld的逆矩阵 //normalize是归一化 fixed3 worldNormal = normalize(mul(v.normal,(float3x3)unity_WorldToObject)); //取得灯光源“位置”信息的单位长度 fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz); //saturate是把结果截取到0~1 //点乘"世界坐标下法线量单位向量"与"世界坐标下灯光位置的单位向量",方向越相同数值越大,这里限制了0~1 //再用受光面的大小来乘上灯光颜色[0~1],即可以得到各个分量的大小 //同里再乘以物体本身的颜色得到最终颜色 fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal,worldLight)); //再加上环境对他的影响 o.color = ambient + diffuse; return o; } float4 frag(v2f i):COLOR{ //上面处理好了,这里直接输出即可 return fixed4(i.color,1); } ENDCG } } }
逐像素着色 计算量会比逐顶点的大,效果也好,但是负担会比较大,区别在于把像素点的讲算放到了fragment方法中。上面例子看明白了,下面这个例子意思是一样的。
Shader "Custom/TestShader7" { Properties{ _Diffuse("Diffuse",Color)=(1,1,1,1) } SubShader{ Pass{ Tags{"LightMode"="ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag #include "Lighting.cginc" fixed4 _Diffuse; //Appction To Vertex struct a2v{ float4 vertex:POSITION; float3 normal:NORMAL; }; //Vertex To Fragment struct v2f{ float4 pos:SV_POSITION; float3 worldnormal:TEXCOORD0; }; v2f vert(a2v v){ v2f o; o.pos=UnityObjectToClipPos(v.vertex); o.worldnormal = normalize(mul(v.normal,(float3x3)unity_WorldToObject)); return o; } float4 frag(v2f i):SV_TARGET{ fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 worldNormal = normalize(i.worldnormal); fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz); fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal,worldLight)); float3 color = ambient + diffuse; return fixed4(color,1); } ENDCG } } }
高光反射例子
逐顶点光照
Shader "Custom/TestShader8" { Properties{ _Diffuse("Diffuse",Color)=(1,1,1,1) _Specular("Specular",Color)=(1,1,1,1) _Gloss("Gloss",Range(8,256))=20 } SubShader{ Pass{ Tags{"LightMode"="ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag #include "Lighting.cginc" fixed4 _Diffuse; fixed4 _Specular; float _Gloss; //Appction To Vertex struct a2v{ float4 vertex:POSITION; float3 normal:NORMAL; }; //Vertex To Fragment struct v2f{ float4 pos:SV_POSITION; float3 color:COLOR; }; v2f vert(a2v v){ v2f o; o.pos=UnityObjectToClipPos(v.vertex); //漫反射部分,和上面的例子一样 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 worldNormal = normalize(mul(v.normal,(float3x3)unity_WorldToObject)); fixed3 worldLightDir = normalize(_WorldSpaceLi 20000 ghtPos0.xyz); fixed3 diffuse = _LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLightDir)); //高光反射 //因为高光反射需要的方向是入射方向,所以需要对光线位置(worldLightDir)进行取反,得到入射方向 //函数reflect(i,n)计算可以反射方向 i:入射方向 n:法线方向 fixed3 reflectDir = normalize(reflect(-worldLightDir,worldNormal)); //取摄像机的位置-顶点的位置=顶点指向摄像机的向量 fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz-mul(unity_ObjectToWorld,v.vertex).xyz); //通过“点乘”“顶点指向摄像机向量”与“反射方向”得到这两向量的投影值 //函数pow(x,y) 返回x的y次方 //"单位点乘"的取值范围在-1~1所以部数越大,范围越小。 fixed3 specular = _LightColor0.rgb * _Specular.rgb *pow(saturate(dot(reflectDir,viewDir)),_Gloss); //最后把颜色加起来 o.color = ambient+diffuse+specular; return o; } float4 frag(v2f i):SV_TARGET{ return float4(i.color,1); } ENDCG } } }
逐像素光照
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' Shader "Custom/TestShader9" { Properties{ _Diffuse("Diffuse",Color)=(1,1,1,1) _Specular("Specular",Color)=(1,1,1,1) _Gloss("Gloss",Range(8,256))=20 } SubShader{ Pass{ Tags{"LightMode"="ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag #include "Lighting.cginc" fixed4 _Diffuse; fixed4 _Specular; float _Gloss; //Appction To Vertex struct a2v{ float4 vertex:POSITION; float3 normal:NORMAL; }; //Vertex To Fragment struct v2f{ float4 pos:POSITION; float3 normal:TEXCOORD0; float3 worldPos:TEXCOORD1; }; v2f vert(a2v v){ v2f o; o.pos=UnityObjectToClipPos(v.vertex); o.normal = normalize(mul(v.normal,(float3x3)unity_WorldToObject)); o.worldPos =mul(unity_ObjectToWorld,v.vertex); return o; } float4 frag(v2f i):SV_TARGET{ //漫反射部分,和上面的例子一样 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); fixed3 diffuse = _LightColor0.rgb*_Diffuse.rgb*saturate(dot(i.normal,worldLightDir)); //高光反射 //因为高光反射需要的方向是入射方向,所以需要对光线位置(worldLightDir)进行取反,得到入射方向 //函数reflect(i,n)计算可以反射方向 i:入射方向 n:法线方向 fixed3 reflectDir = normalize(reflect(-worldLightDir,i.normal)); //取摄像机的位置-顶点的位置=顶点指向摄像机的向量 fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz-i.worldPos.xyz); //通过“点乘”“顶点指向摄像机向量”与“反射方向”得到这两向量的投影值 //函数pow(x,y) 返回x的y次方 //"单位点乘"的取值范围在-1~1所以部数越大,范围越小。 fixed3 specular = _LightColor0.rgb * _Specular.rgb *pow(saturate(dot(reflectDir,viewDir)),_Gloss); //最后把颜色加起来 float3 color = ambient+diffuse+specular; return float4(color,1); } ENDCG } } }
- Shader学习的基础知识(六)透明效果
- Shader学习的基础知识(十三)镜面效果
- Shader学习的基础知识( 三十四)表面着色器上
- Unity Shader入门精要学习笔记 - 第6章 开始 Unity 中的基础光照
- Unity Shader入门精要学习笔记 - 第6章 开始 Unity 中的基础光照
- Shader学习的基础知识(十九)广告牌
- Shader学习的基础知识(十)复杂的灯光概念
- Shader学习的基础知识(二十三)卷积
- Shader学习的基础知识(十二)立方体纹理
- Shader学习的基础知识(二十八)卡通风格渲染
- Shader学习的基础知识(七)深度测试与深度写入
- Shader学习的基础知识(十四)玻璃效果
- Shader学习的基础知识(二十四)深度纹理和法线纹理
- Shader学习的基础知识(十五)程序纹理
- Shader学习的基础知识(二十九)素描风格渲染
- Shader学习的基础知识( 三十一)水波效果
- Shader学习的基础知识(四)法线贴图
- Shader学习的基础知识(八)透明度混合
- Shader学习的基础知识(二十)屏幕后处理效果
- Shader学习的基础知识(九)复杂的灯光概念