当前位置: 首页 > news >正文

武汉平台网站建设排位及资讯

武汉平台网站建设,排位及资讯,揭阳seo快速排名,信用中国 网站截图怎么做渲染管线 概念:GPU绘制物体的时候,标准的,流水线一样的操作 游戏引擎如何绘制物体:CPU提供绘制数据(顶点数据,纹理贴图等)给GPU,配置渲染管线(装载Shader代码到GPU&…

渲染管线

概念:GPU绘制物体的时候,标准的,流水线一样的操作

游戏引擎如何绘制物体:CPU提供绘制数据(顶点数据,纹理贴图等)给GPU,配置渲染管线(装载Shader代码到GPU,配置一次称为一次SetPassCall),并对GPU下命令(DrawCall)绘制。原来的顶点数据需要经过三次矩阵的变换。分别是映射到世界坐标,以摄像机为中心的坐标,投影坐标

纹理坐标:当一个模型建好之后,我们需要给模型上色,上色的过程相当于在一张白纸上面画画,只不过是这张白纸包住了模型,模型顶点在白纸上的坐标就是纹理坐标。

材质其实是配置shader的配置文件,CPU从材质中读取shader数据,把shader程序装载进GPU

顶点数据

模型有一个个三角形面构成,一个面包含三个顶点。
顶点数据包含模型坐标(相对于模型原点),纹理坐标,法线向量,切线向量。

流程

顶点初始化->顶点shader(编程)->Tellellation曲面化->几何shader->裁剪,投影->三角形遍历->片元着色shader(编程)->输出2D图像

顶点初始化:CPU提供顶点数据给GPU,顶点数据包括在模型中的位置,UV纹理坐标,法线,切线等。Unity定义一些装固定数据的"盒子"(SV_XXXX),顶点shader从中拿数据,其他没有固定数据的"盒子"用户可以自由使用

顶点Shader:

  1. 形状变换(可选) 你通过代码,改变顶点的模型坐标
  2. 坐标变换(绘制到准确位置),经过三次坐标变换,映射到2D平面
  3. 数据传递(由编程者决定内容),把数据传回渲染管线,有固定的数据盒子

片元着色shader:
片元是涂色的最小单位,可以看成像素点。每个片元的数据通过顶点数据插值得来,可以引入光照计算。

Unity Shader模板

创建一个unlit Shader,它是一个基础模板,可以开发任意效果:

Shader "Unlit/Shader"
{// 属性列表,可以在编辑器中赋值,用户传递的,不能在shader中修改,在所有着色器可以访问Properties{// 变量名("显示名字",类型)= 默认值_MainTex ("Texture", 2D) = "white" {}} SubShader{Tags { "RenderType"="Opaque" }// 细节距离,离得远不用细节LOD 100// 一个Pass是一个完整的渲染管线绘制Pass{CGPROGRAM    // CG代码的开始#pragma vertex vert   //预编译,告诉编译器顶点代码在哪#pragma fragment frag //预编译,告诉编译器片元着色代码在哪// make fog work#pragma multi_compile_fog// Unity封装的Shader API#include "UnityCG.cginc"struct appdata   // shader中需要使用的数据,Unity定义了一些语义描述来取对应的数据,POSITION,TEXCOORD0对应的是一个个盒子数据,这些数据会先进入顶点shader,可以直接获取{float4 vertex : POSITION;  // 模型坐标,可改float2 uv : TEXCOORD0;     // 开始时候存的纹理坐标,可改};struct v2f  // 给片元shader使用的数据,经过顶点shader计算后得到{float2 uv : TEXCOORD0;UNITY_FOG_COORDS(1)float4 vertex : SV_POSITION;};// 定义用户的属性变量的数据变量,刚好对应2D中的两种数据类型sampler2D _MainTex;   // 图片float4 _MainTex_ST;   // Tilling,offset四个分量// 顶点shaderv2f vert (appdata v){v2f o;// 坐标空间的转换,直接转到投影空间o.vertex = UnityObjectToClipPos(v.vertex);// 纹理坐标叠加上Tilling和offseto.uv = TRANSFORM_TEX(v.uv, _MainTex);UNITY_TRANSFER_FOG(o,o.vertex);// 返回数据给渲染管线return o;}// 片元着色shader,着色调用次数远远大于顶点shader调用次数// 着色好了放到SV_Target// 这个v2f的参数并不等于顶点里面返回的v2f,而是插值后每个片元的v2ffixed4 frag (v2f i) : SV_Target{// sample the texturefixed4 col = tex2D(_MainTex, i.uv);// apply fogUNITY_APPLY_FOG(i.fogCoord, col);return col;}ENDCG}}
}

摄像机如何显示颜色

摄像机看见的颜色=物体本身的发光+反射环境中所有的光
除了太阳,灯泡等物体,大多数是不会自发光的,只会反射光,物体的颜色是因为反射了特定的颜色的光,吸收了其他颜色的光得到的。
自然界的光照是一个非常多次叠加的效果,非常复杂,因此需要简化:不考虑二次反射的光,并且不考虑其他物体的遮挡,但是考虑自己的遮挡。公式变为:
摄像机看见的颜色=物体本身的发光+本色*(直接光照反射+一次反射光+考虑自己的遮挡)+环境光

反射分为镜面反射和漫反射,而环境直接光照和一次反射光可以分为这两种类型,具体类型由材质决定。

每个光都可以用红,绿,蓝,强度表示,例如:
白光(1,1,1,1)
红光(1,0,0,1)
光照的时候,物体的颜色=本色光照
白光照在红物体上:(1,1,1,1)
(1,0,0,1)=(1,0,0,1)=红色
物体本色在建模的时候就决定好了,渲染的好的关键是调节反射
常用的终极简化:

  1. 颜色=本色,休闲小游戏常用,对光照无要求
  2. 颜色=本色*漫反射,移动端常用(兰伯特,半兰伯特)

法线与光照计算

光照计算研究方向有两种:

  1. 基于经验模型(不要求逼真,用于卡通渲染):
    漫反射有 兰伯特,半兰伯特
    镜面反射 有冯高光,布林冯高光

  2. 基于物理PBR(遵循物理定律,能量守恒,模拟真实世界,效果逼真)

法线用于反射,是光照计算的关键。每个片元的法线都可以由顶点法线插值得到。

如何增强模型细节?
方案一:做高模型(面数多,计算量大)
方案二:建一个高模型,只导出法线贴图,但是模型依旧用低模型,但是法线到高模的法线贴图中取,能做出同样的光暗细节。

一个贴图每个点都有对应的RGBA(颜色分量),法线向量可以存储在颜色分量中得到法线贴图。每个法线都是一个标准化向量,通过将三维坐标映射到0到1的范围内通过颜色分量的前三维存储。取出同理,映射和反向映射方法:
(-1,1)*0.5+0.5–>(0,1)
(0,1)*2-1–>(-1,1)

我们也可以只存储x,y,z通过标准化向量来求解得到,可以压缩存储空间。

逐顶点和逐像素光照

这部分计算发生在片元着色shader

逐像素:根据顶点的数据给每个片元插值计算法线,纹理坐标,坐标。然后将这些像素逐个计算反射。这部分计算都在片元着色,这是按照片元的法线计算的。

逐顶点:在顶点shader时候,我们算好顶点的光照颜色(直射光+反射光+环境光),然后通过插值得到每个片元的光照颜色,在片元着色shader的时候只需要将对应纹理的 本色插值的光照 就可以了,不用每个像素使用法线的计算光照了。计算量远远小于逐像素,是移动平台主要采用的方式,但效果可能不能那么细腻。

逐顶点光照:

Shader "Bycw/VertexLight"
{Properties{_MainTex ("Texture", 2D) = "white" {}}SubShader{Tags { "RenderType"="Opaque" "LightMode"="ForwardBase" }LOD 100Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag// make fog work#pragma multi_compile_fog#include "UnityCG.cginc"#include "Lighting.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;float3 normal: NORMAL;};struct v2f{float2 uv : TEXCOORD0;UNITY_FOG_COORDS(1)float4 vertex : SV_POSITION;// 存放片元光照float4 allLight : TEXCOORD1;};sampler2D _MainTex;float4 _MainTex_ST;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);UNITY_TRANSFER_FOG(o,o.vertex);// 计算我们的光照float3 N = normalize(UnityObjectToWorldNormal(v.normal));float3 L = normalize(_WorldSpaceLightPos0);float halfLam = dot(L, N) * 0.5 + 0.5;float3 halfLamLight = halfLam * _LightColor0.rgb;// 把光照传递给片元o.allLight = float4(halfLamLight, 1);// endreturn o;}fixed4 frag (v2f i) : SV_Target{               fixed4 col = tex2D(_MainTex, i.uv);//片元直接本色乘光照col *= i.allLight;// apply fogUNITY_APPLY_FOG(i.fogCoord, col);return col;}ENDCG}}
}

逐片元请看下一小节。

兰伯特漫反射模型

漫反射,是指光照射在粗糙表明向各个方向反射的现象,在这个模型中,向各个方向反射的光强度一致,反射光的强度等于反射光的方向和法线的点乘。因此这个值是0到1的一个范围(负数置0),1代表入射光强度。也就是说光的入射方向如果正好在法线上,那么漫反射的强度等于光照强度。 这也是符合直觉的,在一个平面上,靠近光源的点是最亮的,因为光源刚好在法线上。

为了防止点乘之后的值为负,可以把这个点乘后的值乘0.5,再加0.5,这样就能把范围映射到0到1,这就是半兰伯特反射模型。

这个例子是逐片元光照,对应上一小节的逐顶点:

Shader "Unlit/Shader"
{Properties{_MainTex ("Texture", 2D) = "white" {}}SubShader{// 配置模式Tags { "RenderType"="Opaque" "LightMode"="ForwardBase"}LOD 100Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"// 使用光照头文件#include "Lighting.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;// 拿到模型的法线float3 normal: NORMAL;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;//定义世界坐标系下的法线,因为这里是逐像素(片元),要计算好后传给片元着色shader,如果是逐顶点,就传插值的光照就行float3 wNormal: TEXCOORD1; };sampler2D _MainTex;float4 _MainTex_ST;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);o.wNormal = UnityObjectToWorldNormal(v.normal);return o;}fixed4 frag (v2f i) : SV_Target{// 光照方向的标准化向量float3 L = normalize(_WorldSpaceLightPos0);// 法线向量float3 N = i.wNornal;// 半兰伯特公式float halfLam = dot(L,N) * 0.5 + 0.5// 反射光计算float3 diff = halfLam * _LightColor0.rgb;// 转换单位到4维向量fixed4 diffColor = fixed4(diffLight.rgb,1);fixed4 col = tex2D(_MainTex, i.uv);// 本色 * 光照return col * diffColor;}ENDCG}}
}

冯高光和布林冯高光经验模型

Phong镜面反射模型:
镜面反射在物体表面绝对光滑的情况下,只往法线另一侧相同的夹角反射光线。但是一般没有绝对光滑的物体,因此也会往其他方向反射光线,只是强度会随着偏移反射角而减弱。假设我们定义物体的光滑度为N,标准反射角向量为R,反射点到摄像机的向量为V,则有:

反射光强度 = ( max ⁡ ( 0 , D o t ( R , V ) ) ) N 反射光强度= (\max(0,Dot(R,V)))^N 反射光强度=(max(0,Dot(R,V)))N

Blin-Phone镜面反射模型:

反射光强度 = ( max ⁡ ( 0 , D o t ( H , N ) ) ) N 反射光强度= (\max(0,Dot(H,N)))^N 反射光强度=(max(0,Dot(H,N)))N

其中,N是法线向量,H是视角向量和光源向量的半角向量。也就是说这个向量平分视角向量和光源入射向量,这个向量越接近法线,则视角接收的反射光越强。

Shader "Bycw/BycwPhong"
{Properties{_MainTex ("Texture", 2D) = "white" {}// 属性面板,可以在编辑器中设置_Gloss("Gloss", Range(0, 5)) = 1}SubShader{Tags { "RenderType"="Opaque" "LightMode"="ForwardBase"}LOD 100Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"#include "Lighting.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;// 拿到模型法线float3 normal: NORMAL;};struct v2f{float2 uv : TEXCOORD0;// 世界坐标系下法线 float3 wNormal: TEXCOORD1;// 顶点的世界坐标float3 worldPos: TEXCOORD2;float4 vertex : SV_POSITION;};sampler2D _MainTex;float4 _MainTex_ST;// 属性float _Gloss;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);// 世界法线o.wNormal = normalize(UnityObjectToWorldNormal(v.normal));// 顶点世界坐标o.worldPos = mul(unity_ObjectToWorld, v.vertex);return o;}fixed4 frag (v2f i) : SV_Target{// 片元到光源的标准化向量float3 L = normalize(-_WorldSpaceLightPos0);// 法线float3 N = normalize(i.wNormal);// 反射向量float3 R = reflect(L, N);// 片元到摄像机的向量float3 V = normalize(_WorldSpaceCameraPos - i.worldPos);// float _Gloss = 1;// Lambert计算光强float halfLambert = dot(-L, N) * 0.5 + 0.5;float3 halfLight = halfLambert * _LightColor0.rgb;// Phong高光计算光强float phong = pow(max(0, dot(R, V)), _Gloss); float3 phoneLight = phong * _LightColor0.rgb;// 0.3的漫反射,0.7的镜面反射float4 allLight = float4(phoneLight.rgb, 1) * 0.3 + float4(halfLight.rgb, 1) * 0.7;fixed4 col = tex2D(_MainTex, i.uv);return col * allLight;}ENDCG}}
}

Blin-Phong高光

Shader "Bycw/BlinPhong"
{Properties{_MainTex ("Texture", 2D) = "white" {}_Gloss("Gloss", Range(0, 5)) = 1}SubShader{Tags { "RenderType"="Opaque" "LightMode"="ForwardBase" }LOD 100Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"#include "Lighting.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;float3 normal: NORMAL;};struct v2f{float3 wNormal: TEXCOORD1; float2 uv : TEXCOORD0;float3 worldPos: TEXCOORD2;float4 vertex : SV_POSITION;};sampler2D _MainTex;float4 _MainTex_ST;float _Gloss;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);o.wNormal = normalize(UnityObjectToWorldNormal(v.normal)); o.worldPos = mul(unity_ObjectToWorld, v.vertex);return o;}fixed4 frag (v2f i) : SV_Target{float3 N = i.wNormal;float3 V = normalize(_WorldSpaceCameraPos - i.worldPos);float3 L = normalize(_WorldSpaceLightPos0);// 半角向量float3 H = normalize(L + V);// Blin-Phony光强度float blinPhong = pow(max(0, dot(H, N)), _Gloss);float halfLam = dot(L, N) * 0.5 + 0.5;float3 blinPhoneLight = _LightColor0.rgb * blinPhong;// 半兰伯特光强度float3 halfLamLight = _LightColor0.rgb * halfLam;// 两者的光照叠加float4 allLight = float4(blinPhoneLight, 0.5) + float4(halfLamLight, 0.5);// sample the texturefixed4 col = tex2D(_MainTex, i.uv);return col * allLight;}ENDCG}}
}

法线贴图

次时代中法线贴图是标配

Shader "Bycw/BycwNormal"
{Properties{_MainTex ("Texture", 2D) = "white" {}// 定义法线贴图_NormalTex ("Normal", 2D) = "white" {}}SubShader{Tags { "RenderType"="Opaque" "LightMode"="ForwardBase"}LOD 100Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag// make fog work#pragma multi_compile_fog#include "UnityCG.cginc"#include "Lighting.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;float3 normal: NORMAL;};struct v2f{float2 uv : TEXCOORD0;UNITY_FOG_COORDS(1)float4 vertex : SV_POSITION;float3 wNormal: TEXCOORD1;float3 worldPos: TEXCOORD2;};sampler2D _MainTex;float4 _MainTex_ST;// 法线贴图sampler2D _NormalTex;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);UNITY_TRANSFER_FOG(o,o.vertex);// 可以不写这行o.wNormal = normalize(UnityObjectToWorldNormal(v.normal));o.worldPos = mul(unity_ObjectToWorld, v.vertex);return o;}fixed4 frag (v2f i) : SV_Target{float3 L = normalize(_WorldSpaceLightPos0);// float3 N = i.wNormal;// 法线向量(0,1)解包到(-1,1)得到对应片元的法线向量float3 N = normalize(UnpackNormal(tex2D(_NormalTex, i.uv))); float3 V = normalize(_WorldSpaceCameraPos - i.worldPos);float3 H = normalize(V + L);float _Gloss = 4;float blinPhong = pow(max(0, dot(H, N)), _Gloss);float halfLam = dot(L, N) * 0.5 + 0.5;float3 blinPhoneLight = _LightColor0.rgb * blinPhong;float3 halfLamLight = _LightColor0.rgb * halfLam;// 镜面反射+漫反射+环境光强度float4 allLight = float4(blinPhoneLight, 0.5) + float4(halfLamLight, 0.5) + UNITY_LIGHTMODEL_AMBIENT;// sample the texturefixed4 col = tex2D(_MainTex, i.uv);col *= allLight;// apply fogUNITY_APPLY_FOG(i.fogCoord, col);return col;}ENDCG}}
}

PBR次时代美术工作流

PBR(Physically Based Rendering)是基于物理原理来模拟计算光的反射。算法是成熟的,我们不直接实现其原理,而是调节参数。

PBR有两种工作流,可以相互转换:

  1. 金属与粗糙度工作流:主要元素是颜色贴图,金属度,粗糙度,法线贴图。金属度是模拟光线反射的强度的贴图,例如皮肤是0,盔甲是1。粗糙度是模拟漫反射的贴图,更多细节。这个工作流的优点是更容易创作,各个贴图是分开的,纹理占用内存小(金属和粗糙度贴图是灰度图),更广泛被应用,但是缺点是边缘的伪像更明显,尤其是分辨率低的时候。

  2. 反射与光泽度工作流:接近于真实物理,主要元素是漫反射,反射(RGB贴图),光泽度(RGB贴图),法线贴图。优点是边缘伪像不明显,控制灵活,缺点是灵活控制可能不遵守能量守恒破坏PBR原则,并且RGB贴图多,占用内存多。

高度贴图:类似法线贴图,有着增强细节的效果,把高度值保存起来。

Unity支持两种工作流,Cocos,Laya,UE4只支持主流的金属与粗糙度

美术提供:物体本身贴图,金属度贴图,粗糙度贴图。

在Unity中,名为Standard的shader就是基于PBR的。
其中参数如下:
Albedo:本色贴图
Metallic:金属度,可以调一个值,也可以传贴图
Smoothess:光泽度,可以调值,里面的Source选项中,可以选择粗糙度的来源(本色贴图还是金属度),因为一般金属度和粗糙度用两个通道(R和A)做到一个贴图中。
Normal Map:法线贴图
Height Map:高度贴图
Occlusion:环境遮挡
Detail Mask:细Detail Mask是一种用于控制细节纹理的遮罩或掩模。细节纹理通常用于增加表面的细微细节,例如皮肤上的皱纹、石头上的裂纹等,从而增加物体的真实感。通过定义一个遮罩图像来指定哪些区域应用细节纹理。这个遮罩图像是一个灰度图,其中不同的灰度值表示不同的遮罩强度。通常,灰度值较高的区域将更多地受到细节纹理的影响,而灰度值较低的区域则保留原始的基础材质。
Emission:自发光,可以选择颜色或贴图

渲染队列与ZTest,ZWrite

普通情况下,绘制画面时,绘制最远处的物体,然后绘制近处物体,这样就能覆盖掉远处物体。但是绘制的时候做了优化,先绘制近的,再绘制远的,如果远的被挡住了,就不进行绘制,远近不是相对于一整个物体来说的,而是相对于一个个片元。
先绘制的物体会把片元放入颜色缓存区和深度缓冲区,绘制后面的片元的时候,比较深度,如果深度比原来的小,那么就把这个新的片元放入原来的颜色缓冲区和深度缓冲区。如果不是,这个片元信息会被丢弃,称为没有通过深度测试(ZTest)。把片元信息更新到深度缓存区称为ZWrite。如果关掉深度缓存,虽然颜色会更新,但是深度不会更新。
深度测试有不同规则:
ALWAY:颜色都会通过测试
NEVER:颜色永远无法通过测试
LESS:距离摄像机越近的通过测试
EQUAL:距离相等的通过测试
GREATER:距离摄像机越远的通过测试
NOT_EQUAL:距离不相等的通过测试

如果一个物体的shader把深度测试设定为ALWAY,那么永远都能绘制到屏幕中。

绘制透明物体(第四维的透明度数值alpha不是1)的时候,可以采用不同的渲染队列,每个队列有不同的编号,场景的物体会根据队列存放。游戏引擎先绘制编号小的队列,再绘制编号大的队列,不同的队列都有不同的颜色和深度缓冲区。
不同队列直接的颜色不会被遮挡,但是会混合,所以通过不同渲染队列透明物体看其他物体时是两种物体的混合色。这个过程叫Blend
Blend有几种混合模式:

  1. 直接覆盖(默认)
  2. 透明度混合(常用)
// 根据后面绘制物体的透明度混合,例如后面队列的透明物体Alpha为30%,则按照30%透明物体颜色和70%缓存的物体颜色绘制,src是本色,dst是颜色缓存
Blend SrcAlpha OneMinusSrcAlpha
// 线性叠加
Blend One One
// 正片叠底,相乘Multiply
Blend DstColor Zero

通过在透明物体shader上设置上面代码可以达到对应效果

立方体纹理采样和自制天空盒

天空盒其实就是一个立方体或球体,立方体有六个面,每个面都可以对应一个纹理贴图。如果这六个贴图在边缘处是连续的,那么就可以看成一个整体。
对于天空盒来说,其渲染颜色只由本色决定,创建天空盒(立方体或球体),包围住整个场景,并编写Shader控制渲染盒子,然后调整渲染队列顺序(调到最小),使其最先绘制。
我们需要创建一个Cubemap的纹理,它可以设置立方体的六张贴图。

Shader "Bycw/BycwSkyBox"
{Properties{// 这是Cube纹理,不是一般的纹理_MainTex ("Texture", Cube) = "white" {}}SubShader{Tags { "RenderType"="Opaque" }LOD 100Cull Off     // 背对摄像机的面不会被裁掉;ZWrite Off   // 关掉深度缓存Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{// 立方体原点指向顶点的向量float3 dir : TEXCOORD0;float4 vertex : SV_POSITION;};// 纹理贴图samplerCUBE _MainTex; float4 _MainTex_ST;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);// 通过模型顶点得到向量o.dir = v.vertex;return o;}fixed4 frag (v2f i) : SV_Target{// i.uv变成i.dir了fixed4 col = texCUBE(_MainTex, i.dir);// fixed4 col = fixed4(1.0, 0, 0, 1.0);return col;}ENDCG}}
}

向前渲染管线

向前渲染:一种Unity内置的渲染管线,其包含多光源的光照计算,分为两个部分:base和additional:

  1. base:包含最重要的逐像素光源+逐顶点光源+SH光源+LightMap+环境光,称为一个Pass
  2. Additional:其他重要的逐像素光源,一个光源一个Pass

如何处理多光源光照计算:
每个光源可以设置渲染模式为:Auto,Important,Not Important
Important是逐像素光照,Not Important是逐顶点光照和SH光源的计算。Important光源的数量决定Pass的数量。因此每个重要光源都会做一次光照计算。
float4 _LightColor0:该Pass处理的重要光源的颜色
float4 _WorldSpaceLightPos0:重要光源的位置,如果是平行光,_WorldSpaceLightPos0.w为0,其余为1
float4x4 LightMatrix0:从世界空间到光源空间的变换矩阵,可以用于采样cookie和光强衰减(attenuation)纹理
unity_4LightPosX0:
unity_4LightPosY0:
unity_4LightPosZ0:仅用于BasePass,前四个非重要点光源在世界空间中的位置
float4 unity_4LightAtten0:仅用于BasePass,储存4个非重要点光源的刷减因子

float4 unity_4LightColor:仅用于BasePass,储存4个非重要点光源的颜色

Shader "Bycw/BycwForward"
{Properties{_MainTex ("Texture", 2D) = "white" {}}SubShader{LOD 100Pass{// 向前渲染Tags { "RenderType"="Opaque" "LightMode"="ForwardBase" }CGPROGRAM#pragma vertex vert#pragma fragment frag//预编译指令,用于前向渲染计算basePass#pragma multi_compile_fwdbase#include "UnityCG.cginc"#include "Lighting.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;};sampler2D _MainTex;float4 _MainTex_ST;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);return o;}fixed4 frag (v2f i) : SV_Target{// sample the texturefixed4 col = tex2D(_MainTex, i.uv);//unity_LightColor存储四个非重要光源颜色,_LightColor0是本Pass唯一重要的光源float4 lightColor = _LightColor0 + unity_LightColor[0] + unity_LightColor[1] + unity_LightColor[2] + unity_LightColor[3];// 本身乘光照col = col * lightColor;return col;}ENDCG}Pass{// 一个其他重要的光源Tags { "RenderType"="Opaque" "LightMode"="ForwardAdd" }// 混合光照Blend One OneCGPROGRAM#pragma vertex vert#pragma fragment frag// 预编译,其他重要的光源#pragma multi_compile_fwdadd#include "UnityCG.cginc"#include "Lighting.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;};sampler2D _MainTex;float4 _MainTex_ST;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);return o;}fixed4 frag (v2f i) : SV_Target{// sample the texturefixed4 col = tex2D(_MainTex, i.uv);// fixed4 col = fixed4(1, 1, 1, 1);col = col * _LightColor0;return col;}ENDCG}}}

渲染管线设置

每个摄像机都会绘制一个场景,因此渲染管线的设置在摄像机上:

Rendering Path:Use Graphics Setting:默认是向前渲染,在Player Setting->Graphics->Randing Path中设置Forward:向前渲染Delay:演示渲染

重要光源数目可以在Project Setting->Quality->Pixel Light Count设置

光照衰减计算

计算方式:
点光源:球体衰减,中心是1,超过最远距离是0
聚光灯:垂直衰减和水平衰减
衰减系数可以通过计算,也可以事先存到纹理中读取(主流)

Shader "Bycw/BycwLightAtten"
{Properties{_MainTex ("Texture", 2D) = "white" {}}SubShader{LOD 100Pass{Tags { "RenderType"="Opaque" "LightMode"="ForwardBase" }CGPROGRAM#pragma vertex vert#pragma fragment frag#pragma multi_compile_fwdbase#include "UnityCG.cginc"#include "Lighting.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;};sampler2D _MainTex;float4 _MainTex_ST;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);return o;}fixed4 frag (v2f i) : SV_Target{// sample the texturefixed4 col = tex2D(_MainTex, i.uv);// 平行光的衰减因子float atten = 1.0;col = col * _LightColor0 * atten;return col;}ENDCG}Pass{Tags { "RenderType"="Opaque" "LightMode"="ForwardAdd" }Blend One OneCGPROGRAM#pragma vertex vert#pragma fragment frag#pragma multi_compile_fwdadd#include "UnityCG.cginc"#include "Lighting.cginc"// 需要这个头文件#include "AutoLight.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;float3 worldPos: TEXCOORD1;};sampler2D _MainTex;float4 _MainTex_ST;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);o.worldPos = mul(unity_ObjectToWorld, v.vertex);return o;}// 重要光源;fixed4 frag (v2f i) : SV_Target{   /*     float atten = 1.0;#ifdef USING_DIRECTIONAL_LGITHfixed atten = 1.0;#else//点光源#if defined(POINT)// 把片元的世界坐标转移到以点光源为参考的坐标float3 lightCoord = mul(unity_WorldToLight,float4(i.worldPos,1)).xyz;//使用点到光源的距离值的平方来取样,可以避开开方操作//使用宏UNITY_ATTEN_CHANNEL来得到衰减纹理中衰减值所在的分量,以得到最终的衰减值。fixed atten = tex2D(_LightTexture0,dot(lightCoord,lightCoord).rr).UNITY_ATTEN_CHANNEL;//聚光灯#elif defined(SPOT)float4 lightCoord = mul(unity_WorldToLight,float4(i.worldPos,1));//角度衰减 * 距离衰减// _LightTexture0是衰减系数纹理fixed atten = (lightCoord.z > 0) * tex2D(_LightTexture0,lightCoord.xy/lightCoord.w + 0.5).w * tex2D(_LightTextureB0,dot(lightCoord,lightCoord).rr).UNITY_ATTEN_CHANNEL;#elsefixed atten = 1.0;#endif#endif*/// 采用Unity内置的宏计算,原理和上面代码一致UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);// 自己算// float r = distance(i.worldPos, _WorldSpaceLightPos0);// float atten = 1- (r / 10);// endfixed4 col = tex2D(_MainTex, i.uv);col = col * _LightColor0 * atten;return col;}ENDCG}}
}

阴影计算原理

步骤:

  1. 计算阴影区域
  2. 在阴影区域叠加颜色

计算区域的时候,光源相当于摄像机,光源看不见的地方没有阴影。Shader中会编写一个特殊的Pass,计算阴影时调用。我们需要把片元坐标转换为以光源为中心的坐标,并通过光源计算每个片元的深度信息,如果某个地方的深度比光源照到的地方大,那么就是阴影。这个深度信息会提供给引擎,通过其他shader接收阴影。

在物体属性Mesh Renderer中,可以在Lighting中调节Cast Shadows,如果关闭,物体阴影投射的pass不会被调用,也就没有阴影。Receive Shadows可以设置是否接收阴影。
物体投射阴影的时候,会产生Pass,因此DrawCall会加1

Shader "Bycw/BycwShadow"
{Properties{_MainTex ("Texture", 2D) = "white" {}}SubShader{Tags { "RenderType"="Opaque" }LOD 100// 投影Pass, 摄像机在我们光源的位置;Pass {// 标注模式为阴影投射Tags {"LightMode"="ShadowCaster"}CGPROGRAM#pragma vertex vert#pragma fragment frag// 预编译#pragma multi_compile_shadowcaster#include "UnityCG.cginc"struct v2f {                V2F_SHADOW_CASTER; //宏相当于 float3 vec : TEXCOORD0; float4 pos : SV_POSITION};v2f vert(appdata_base v) {v2f o;TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);// 相当于// o.vec = mul(unity_ObjectToWorld, v.vertex).xyz - _LightPositionRange.xyz;//opos = UnityObjectToClipPos(v.vertex)return o;}float4 frag(v2f i): SV_Target {SHADOW_CASTER_FRAGMENT(i)// 相当于// return UnityEncodeCubeShadowDepth((length(i.vec) + unity_LightShadowBias.x) * _LightPositionRange.w)// 把深度信息传出去了}ENDCG    }// Base PassPass{Tags {"LightMode"="ForwardBase"}CGPROGRAM#pragma vertex vert#pragma fragment frag#pragma multi_compile_fwdbase#include "UnityCG.cginc"#include "Lighting.cginc"#include "AutoLight.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 pos : SV_POSITION;// 根据阴影坐标,你可以获取深度信息,获取阴影的颜色值;SHADOW_COORDS(1) // unityShadowCoord4 _ShadowCoord : TEXCOORD1;};sampler2D _MainTex;float4 _MainTex_ST;v2f vert (appdata v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);// 算出阴影坐标TRANSFER_SHADOW(o); // o._ShadowCoord = ComputeScreenPos(o.pos);return o;}fixed4 frag (v2f i) : SV_Target{fixed4 col = tex2D(_MainTex, i.uv);// 根据我们的阴影的坐标,把阴影的数据获取出来,得到阴影颜色fixed shadow = SHADOW_ATTENUATION(i); // unitySampleShadow(i._ShadowCoord)// 光的颜色乘以阴影(0到1的值,表示光线在阴影区域的衰减程度或透明度)float4 lightColor = _LightColor0 * shadow;return col * lightColor;}ENDCG}}}

GPU并发计算(compute shadder)

GPU的线程和CPU不同,线程由硬件实现,每个线程都有自己的执行上下文和寄存器,可以单独执行计算任务,而这种计算单元有成千上万个,可以实现高效的并行计算。CPU缓存大、逻辑运算ALU较少;GPU逻辑运算较多,缓存较小。相较之下,GPU有更多的运算单元,即干活的人多了。GPU 包含数千个并行计算单元,称为 CUDA 核心。这些 CUDA 核心可以同时处理多个数据流,从而实现高效的并行计算。CUDA 核心还包含了一些特殊的硬件单元,例如浮点数处理单元、整数处理单元、逻辑单元和共享内存等,可以提供快速的数学运算和数据处理能力。
GPU中,线程被组织成线程组,每个线程组包含多个线程,维度可以用numthreads(x,y,z)设置,GPU会根据线程组的大小和维度自动分配线程资源,并将计算任务分配给每个线程

案例:控制GPU计算1024个sqrt计算

GPU代码:Unity中创建Compute Shader,GPU的代码指令中有Kernel的概念,并行计算的函数入口都可以放到kernel里面。
例如下面的函数CSSqrtMain会排到kernel0,每个这样的函数都有一个KernalIndex

// Each #kernel tells which function to compile; you can have many kernels
#pragma kernel CSSqrtMainRWStructuredBuffer<float> inputData; // 用于接受CPU传递过来的数据
RWStructuredBuffer<float> outputData; // 用于返回给GPU的数据;// 分配线程组
[numthreads(64,1,1)]
void CSSqrtMain(uint3 id : SV_DispatchThreadID)// id是三维的
{outputData[id.x] = sqrt(inputData[id.x]);
}

常见的读写缓存区数据类型如下:
RWBuffer:这个类型是用于读写一维数据的缓冲区类型,可以用于存储任意类型的数据,例如float、int等。可以使用SetData和GetData函数将数据写入和读出缓冲区。
RWByteAddressBuffer:这个类型也是用于读写一维数据的缓冲区类型,但它是按字节寻址的,可以用于存储任意类型的数据。可以使用SetData和GetData函数将数据写入和读出缓冲区。
RWStructuredBuffer:这个类型是用于读写结构化数据的缓冲区类型,可以用于存储自定义的结构体类型。可以使用SetData和GetData函数将数据写入和读出缓冲区。
RWTexture1D:这个类型是用于读写一维纹理数据的缓冲区类型,可以用于存储颜色或其他类型的数据。可以使用SetPixel和GetPixel函数将数据写入和读出缓冲区。
RWTexture1DArray:这个类型是用于读写一维纹理数组数据的缓冲区类型,可以用于存储颜色或其他类型的数据。可以使用SetPixel和GetPixel函数将数据写入和读出缓冲区。
RWTexture2D:这个类型是用于读写二维纹理数据的缓冲区类型,可以用于存储颜色或其他类型的数据。可以使用SetPixel和GetPixel函数将数据写入和读出缓冲区。
RWTexture2DArray:这个类型是用于读写二维纹理数组数据的缓冲区类型,可以用于存储颜色或其他类型的数据。可以使用SetPixel和GetPixel函数将数据写入和读出缓冲区。
RWTexture3D:这个类型是用于读写三维纹理数据的缓冲区类型,可以用于存储颜色或其他类型的数据。可以使用SetPixel和GetPixel函数将数据写入和读出缓冲区。

CPU传递数据需要使用ComputeBuffer:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class MySqrtGPU : MonoBehaviour
{private static int itemCount = 1024;// 编辑器绑定GPU脚本public ComputeShader mysqrtGPUShader = null;ComputeBuffer inputData = null;ComputeBuffer outputData = null;void Start(){inputData = new ComputeBuffer(itemCount, sizeof(float));outputData = new ComputeBuffer(itemCount, sizeof(float));float[] rawInputData = new float[itemCount];for (int i = 0; i < itemCount; i++) {rawInputData[i] = (float)(i);}// 设置传递数据inputData.SetData(rawInputData);// 查找对应的KernalIndexint kernelIndex = this.mysqrtGPUShader.FindKernel("CSSqrtMain");// 关联数据this.mysqrtGPUShader.SetBuffer(kernelIndex, "inputData", inputData);this.mysqrtGPUShader.SetBuffer(kernelIndex, "outputData", outputData);// 执行对应kernelIndex的函数this.mysqrtGPUShader.Dispatch(kernelIndex, itemCount / 64, 1, 1);// 取出结果float[] result = new float[itemCount];outputData.GetData(result);for (int i = 0; i < itemCount; i++) {Debug.Log("sqrt(" + i + ") = " + result[i]);}}
}

查看Unity内置shader源码

查看Unity内置shader源码是我们学习shader的重要资源,可以在Unity官网上的Built in shaders下载

http://www.fp688.cn/news/145593.html

相关文章:

  • 建立一个网站如何开通账号seo优化工作
  • 合肥网站建设优化乐山网站seo
  • wordpress 4.1重庆seo网站推广优化
  • SharePoint做网站好吗百度推广计划
  • 加强政务公开网站建设优化大师优化项目有哪些
  • 技术支持 网站建设秦洁婷seo博客
  • 用php做网站的优势抖音seo软件
  • 建立可以在线做照片的网站西安网站建设排名
  • 公路机电工程建设网站哈尔滨网络推广优化
  • 网站规划对网站建设起到什么作用微信小程序开发平台官网
  • wordpress gateway金华seo全网营销
  • 自贡企业网站建设四川seo关键词工具
  • 图片设计网站免费网络营销的手段有哪些
  • phpstudy做网站常熟seo网站优化软件
  • 无极在线网站播放找网络公司做推广费用
  • 天津网站建设案例百度关键词优化和百度推广
  • 网页设计教程dw东莞搜索优化十年乐云seo
  • 经典网站设计网络广告营销的特点
  • 优秀网站配色seo是什么职业做什么的
  • 新开传奇网站超变有实力的网站排名优化软件
  • 这样自己做网站百度搜索引擎的优缺点
  • 怎么在自己做网站如何实现网站的快速排名
  • 徐州网架加工seo优化的基本流程
  • 表格我做视频网站百度惠生活商家怎么入驻
  • 做网站用哪里的服务器比较好关键词优化排名要多少钱
  • 有哪些做特卖的网站有哪些湖南seo优化服务
  • jsp做网站毕业设计郑州seo外包顾问
  • 大学网站 作风建设专题天津seo结算
  • 建一个免费网站百度推广怎么弄
  • 简述网站开发的基本流程seo的优点