IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> Unity Shader入门精要初级篇(一)-- 基础光照笔记 -> 正文阅读

[游戏开发]Unity Shader入门精要初级篇(一)-- 基础光照笔记

一篇个人Shader笔记,主要是通过分析Shader代码来总结几个光照模型。

0. 标准光照模型

标准模型主要是分为四个部分,自发光 + 高光反射 + 漫反射 + 环境光
根据不同的光照模型,计算不同的光线进行相加

逐顶点和逐像素

模型光照计算主要分为两种,逐像素和逐顶点 ,即Grouraud Shading 和 Phone Shading

逐顶点

在每个顶点上计算光照,在渲染图元内部进行线性插值,最后输出成像素颜色
缺点:顶点计算光照内部进行插值,图元内部总会比顶点处最高颜色值更暗,而且会出现棱角

不过这也告诉我们,光照计算在顶点着色器

逐像素

以每个像素为基础,得到他的法线, (可以是顶点法线插值,可以是从法线纹理中采样),进行光照计算
因此,光照计算在片元着色器

1.漫反射模型

Lambert漫反射模型的基础,主要是满足Lambert定律:即反射光线的强度与表面法线和光源方向之间的夹角的余弦值成正比

写成公式就是:
Cdiffuse = ( Clight · mdiffuse )max (0, n · l)
Clight :光源颜色
mdiffuse:材质漫反射颜色
n : 表面法线
l : 指向光源单位矢量

1.1 逐顶点漫反射

mdiffuse材质的漫反射颜色的定义,颜色可以在unity中自取

	Properties
    {
        _Diffuse("Diffuse", Color) = (1, 1, 1, 1)
    }

分别是a2v和v2f,即application to vertex shader 和 vertex shader 到 fragment shader 的定义。

在顶点着色器中计算光照,因此需要在a2v中访问到顶点和法线;要将顶点着色器中的光照颜色传递给片元着色器,因此,v2f中需要将顶点转换裁剪空间和储存color值

            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
            };

这就是顶点着色器中具体的计算了。Clight和mdiffuse一个是直接得到一个是之前定义了,需要解决的只有n · l 。需要把这两个值都先转换到世界空间进行点乘。

世界坐标下的法线:unity内置的 unity_WorldToObject 可以从世界坐标转换到局部坐标,那么运用矩阵的逆运算,得到法线的世界坐标。

其他的带入公式

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 worldLight = normalize(_WorldSpaceLightPos0.xyz);
    fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb *saturate(dot(worldNormal,worldLight));
    
	o.color = ambient + diffuse;

	return o;
}

片元着色器中直接输出就好了

            fixed4 frag(v2f i) : SV_TARGET
            {
                return fixed4(i.color, 1.0);
            }

1.2 逐像素漫反射

主要是两个着色器中计算的内容不一样。


在顶点着色器中,不需要计算光照,只需要把世界空间下的顶点和法线传递给片元着色器即可。

v2f vert(a2v v)
{
    v2f o;
    o.pos = UnityObjectToClipPos(v.vertex);

    o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);

    return o;
}

重点都在片元着色器中计算光照了。,思路都是一致的,注意各种矢量的单位化。

fixed4 frag(v2f i) : SV_TARGET
{
	   fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
	
	   fixed3 worldNormal = normalize(i.worldNormal);
	   fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
	
	   fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
	
	   fixed3 color = ambient + diffuse;
	
	   return fixed4(color, 1.0);
}

2. 高光反射模型

高光反射模型分Phone 和 Blinn Phone 两种
前者模型方式是,高光反射与视角方向和反射方向的点乘值相关, 后者认为是法线方向与视角方向的半程向量与法线的点乘值相关。

Phone高光模型:
Cspecular = (Clight · mspecular) max(0, v, r)mgloss
Clight : 入射光线的颜色与强度
mspecular: 材质的高光反射系数
v: 视角方向
r : 反射方向
mgloss: 光泽度,反光度

Blinn Phone高光模型:
Cspecular = (Clight · mspecular) max(0, n, h)mgloss
n : 法线,h 是半程向量
其他大致是一样的

2.1 逐顶点Phone高光反射

漫反射颜色,高光反射系数和光泽度都是自定义的如下

    Properties
    {
        _Diffuse("Diffuse", Color) = (1, 1, 1, 1)
        _Specular("Specular", Color) = (1, 1, 1, 1)
        _Gloss ("Gloss", Range(8.0, 256)) = 20
    }

在顶点着色器计算光照,主要是计算高光,所以环境光我们直接获取。漫反射的计算方式不多写了,高光带入公式一顿计算,加上环境光返回

reflectDir是直接通过内置函数,写入光线入射方向和顶点法线方向得到出射方向
viewDir 通过相机的世界位置 - 顶点的世界位置得到视野向量

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(_WorldSpaceLightPos0.xyz);
    
    fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));

    fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));

    fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz);
    fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);

    o.color = ambient + diffuse + specular;
    return o;

最后再片元着色器中直接将颜色输出就好,与上面的一致。


2.2 逐像素Phone高光反射

显然是在片元着色器中计算光照,思路一致。在顶点着色器中,只需要计算出顶点的世界坐标和法线的世界下的位置

fixed4 frag(v2f i) : SV_TARGET
{
    fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
    fixed3 worldNormal = normalize(i.worldNormal);
    fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);

    fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
    fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));

    fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
    fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);

    return  fixed4(ambient + diffuse + specular, 1.0);
}

2.3 逐像素Blinn Phone 高光模型

大致一样的,主要是片元计算光照,多一个半程向量的计算并使用

fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(reflectDir, viewDir)), _Gloss);
  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-07-03 11:07:46  更:2022-07-03 11:08:49 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 10:40:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码