当前位置:网站首页>【图形学】18 光照模型(三、镜面反射的Shader实现)
【图形学】18 光照模型(三、镜面反射的Shader实现)
2022-08-08 06:25:00 【纸境止境】
来源:《UNITY SHADER入门精要》
1、镜面反射ShaderLab的方式
我们回忆之前的公式:
c s p c u l a r = ( c l i g h t ⋅ m s p e c u l a r ) max ( 0 , v ^ ⋅ r ) m g l o s s \boldsymbol{c}_{spcular}=\left( \boldsymbol{c}_{light}\cdot \boldsymbol{m}_{specular} \right) \max \left( 0, \hat{v}\,\,\cdot \,\,\boldsymbol{r} \right) ^{m_{gloss}} cspcular=(clight⋅mspecular)max(0,v^⋅r)mgloss
我们计算镜面反射需要四个参数:入射光线 c l i g h t \boldsymbol{c}_{light} clight 、材质的高光反射系数 m s p e c u l a r \boldsymbol{m}_{specular} mspecular 、视角方向 v ^ \hat{v} v^ 、反射方向 r \boldsymbol{r} r 。而其中,反射方向 r \boldsymbol{r} r 又需要由表面法线 n ^ \hat{n} n^ 和 光源方向 I \boldsymbol{I} I 计算而得。
公式不难写出,不过,CG提供了计算反射方向的函数:reflect(i, n)。其中参数:i 为入射方向;n 为法线方向。
2、逐顶点的镜面反射
Shader "Unity Shaders Book/Chapter 6/Specular Vertex-Level" {
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" }
这里定义了三个 Properties ,其中,_Diffuse 用来控制漫反射的颜色系数, _Specular 用来控制高光反射的颜色, _Gloss 用来控制高光区域的大小。
这里的 LightMode 设置为 ForwardBase 定义了该 Pass 在 Unity 的光照流水线中的角色。
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
同样,在CGPROGRAM之后,我们包含头文件"Lighting.cginc",用 #pragma 指定顶点/片元着色器的函数。
声明 Properties 语义块中的属性:由于颜色属性的范围在0到1之间,因此对于Diffuse和Specular属性我们可以使用fixed精度的变量来存储它。而Gloss的范围很大,因此我们使用flot精度来存储。
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
fixed3 color : COLOR;
};
定义输入和输出要使用的结构体
v2f vert(a2v v) {
v2f o;
// Transform the vertex from object space to projection space
o.pos = UnityObjectToClipPos(v.vertex);
// Get ambient term
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
// Transform the normal from object space to world space
fixed3 worldNormal = normalize(mul((float3x3)unity_ObjectToWorld, v.normal));
// Get the light direction in world space
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
// Compute diffuse term
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
前面的计算是关于漫反射的,跟之前是一致的。
// Get the reflect direction in world space
fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
// Get the view direction in world space,计算的是每一个顶点在世界空间的位置。
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz);
// Compute specular term
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
o.color = ambient + diffuse + specular;
return o;
}
fixed4 frag(v2f i) : SV_Target {
return fixed4(i.color, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
这里采用的 CG 的 reflect() 函数计算了反射光线。由于参数要求传入的方向是从光源到交点处的,所以,采用的是它的反向量。
然后,我们用计算得到的反射向量,计算得到高光值,然后都相加起来,返回就好了。
最后,第21行,我们磨人的FallBack设置为 内置的 Specular。
最后,我们注意到,逐顶点光照,由于线性插值的缘故,会有明细的线性效果,不够真实。所以,我们需要逐像素来出来计算。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oIf2liTz-1659590622745)(assets/image-20220620162252379.png)]
3、逐像素光照计算
Shader "Unity Shaders Book/Chapter 6/Specular Pixel-Level" {
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;
// Transform the vertex from object space to projection space
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
// Transform the normal from object space to world space
o.worldNormal = mul(v.normal, (float3x3)_World2Object);
// Transform the vertex from object spacet to world space
o.worldPos = mul(_Object2World, v.vertex).xyz;
return o;
}
顶点着色器所做的事情就是计算 世界空间下的法线方向和顶点坐标,并把它们传递给片元着色器。
fixed4 frag(v2f i) : SV_Target {
// Get ambient term
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
// Compute diffuse term
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
// Get the reflect direction in world space
fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
// Get the view direction in world space
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
// Compute specular term
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
边栏推荐
猜你喜欢

Consumer Goods Industry Report: Research Analysis and Development Prospect Forecast of Cosmetic Container Market Status

略解深度学习优化策略

MySQL高级

Flutter学习开发资源整理与分享

Distributed voltage regulation using permissioned blockchains and extended contract net protocol to optimize efficiency

访问修饰符public、private、protected、default(默认不写) 区别

Accelerate CNNs from Three Dimensions: A Comprehensive Pruning Framework详解

Flutter 实现一款简单的音乐播放器

P17 五子棋的实现4 悔棋功能

C语言实现顺序表的九个常用接口
随机推荐
Equipment industry research report: laser printer market present situation and development trend in the future
链式队列的入栈和出栈相关操作
文件常用操作 IO流原理及分类
栈队列OJ题分享及讲解
2.Explain详解与索引优化原则
Flutter学习开发资源整理与分享
Mybaits笔记
CSDN21天学习挑战赛之顺序查找
NVIDIA CUDA 高度并行处理器编程(七):并行模式:前缀和
关于剪枝对象的分类(weights剪枝、神经元剪枝、filters剪枝、layers剪枝、channel剪枝、对channel分组剪枝、Stripe剪枝)
P04 并发小球V1 V2.1
[GWCTF 2019] I have a database 1
线程P01——进程 并发 并行 线程的使用
3.多线程两种实现方式的区别
ResNet 原理与代码复现
二叉树代码练习
Part 10:iOS的数据持久化(2),Sqlite,CoreData
jupyter notebook添加目录
C语言实现链表的增删查改以及OJ题讲解
Electronic payment market status quo of the study: 2022 volume is expected to increase to 314.1 billion yuan