学习第六章Unity内置函数时,由于之前使用mul矩阵乘法时的顺序与书中不一致,导致使用内置函数时出现光照效果不一样,因此引出以下两个问题:

  1 什么时候使用3x3矩阵,什么时候使用4x4矩阵?

  2 法线变换矩阵与坐标变换矩阵不相同

  解答1:

  4.9.1节书中讲述了何时使用3x3和4x4矩阵。因为4x4矩阵是比3x3矩阵多了平移变换,因此对空间坐标进行变换时,通常使用4x4矩阵。而对于切线和法线这两种空间矢量,不存在平移的情况,因此仅使用3x3矩阵即可(是否可以偷懒使用4x4矩阵?是可以的)。

  解答2:

  而对于法线变换,因为对坐标缩放时,有可能不是等比缩放,这会导致法向量使用坐标变换矩阵后不再垂直于表面。有下述推导过程求得法线变换矩阵:

  假设某平面点的模型空间切线为T,法线为N。通过空间转换M,法线转换G后,切线为T',法线为N'。那么有:

  TT·N = 0 ----- (1),

  (T')T·N' = 0 -----(2),

  T' = M·T -----(3),

  N' = G·N -----(4)。

  由式(2)(3)(4)有:

(M·T)T·(G·N) = 0  ==>  TT·MT·G·N = 0 ==>  (矩阵结合律)  (TT·MT·G)·N = 0  -----(5)

  结合式(1)和(5),得到:T= TT·MT·G,由此得到 MT·G = I,则:

G = (MT)-1 =   (M-1)T

  即法线的变换矩阵,是空间变换矩阵的转置的逆。

  

======================Unity Shader中计算注意事项=======================

1  在Unity Shader中,将坐标从模型空间转换到世界空间,使用如下方式:

v2f o;
o.worldPos = mul(_Object2World, v.vertex).xyz;

  UnityShader中的转换矩阵为行矩阵,模型空间坐标v.vertex作为列矩阵,与转换矩阵右乘,得到世界坐标。

  注意,这里不能使用 o.worldPos = UnityObjectToWorldDir(v.vertex); 在UnityCG.cginc中查看UnityObjectToWorldDir的定义:

// Transforms direction from object to world space
inline float3 UnityObjectToWorldDir( in float3 dir )
{
return normalize(mul((float3x3)_Object2World, dir));
}

  这里使用的是3x3矩阵,使得坐标变换丢失了平移参数。

2  将法线从模型空间转换到世界空间,使用如下方式:

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

  在这里,矩阵_World2Object是矩阵_Object2World的逆矩阵。由于是法向量,因此取3x3矩阵进行计算。

  需要注意的是,这里的mul实现的矩阵乘法,当向量在左侧时,此向量相当于行向量,其中数值是与矩阵的列元素进行乘法与加法。相当于如下写法:

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

  即法线的变换是空间变换_Object2World的逆矩阵的转置transpose(_World2Object)。

  这里Unity Shader封装了一个函数来转换法线到世界坐标UnityObjectToWorldNormal(),替代上面的矩阵乘法写法,不容易出错。

o.worldNormal = UnityObjectToWorldNormal(v.normal);

 

// Transforms normal from object to world space
inline float3 UnityObjectToWorldNormal( in float3 norm )
{
// Multiply by transposed inverse matrix, actually using transpose() generates badly optimized code
return normalize(_World2Object[].xyz * norm.x + _World2Object[].xyz * norm.y + _World2Object[].xyz * norm.z);
}

================书中完整示例===============

Shader "Unity Shaders Book/Chapter 6/Blinn-Phong Build-In Function"
{
Properties
{
_Diffuse("Diffuse", Color) = (1.0, 1.0, 1.0, 1.0)
_Specular("Specular", Color) = (1.0, 1.0, 1.0, 1.0)
_Gloss("Gloss", Range(8.0, 256.0)) = 20.0
}
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;
fixed3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
}; v2f vert (a2v v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal); // 效果与下一行相同,因为是矢量变换,用3x3变换矩阵即可。
// o.worldNormal = mul(v.normal, (float3x3)_World2Object);
// o.worldPos = UnityObjectToWorldDir(v.vertex); // normalize(mul((float3x3)_Object2World, dir));效果与下一行不同
// o.worldPos = mul(v.vertex, transpose(_Object2World)).xyz; // 将坐标左乘,将模型空间坐标转换到世界空间,与下式相等
o.worldPos = mul(_Object2World, v.vertex).xyz; // 将坐标右乘,将模型空间坐标转换到世界空间
return o;
} fixed4 frag(v2f i) : SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
fixed3 halfDir = normalize(viewDir + worldLightDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(, dot(halfDir, worldNormal)) , _Gloss);
fixed3 color = ambient + diffuse + specular;
return fixed4(color, 1.0);
} ENDCG
}
} Fallback "Specular"
}

使用Unity5 build-in的函数实现Blinn-Phong光照模型。

[Unity Shader] 坐标变换与法线变换及Unity5新增加的内置函数的更多相关文章

  1. Unity3D -- shader语法内置函数

    该篇是Unity Shader中HLSL的内置函数,主要是一些数学方面的计算函数.在写Shader的时候可以直接使用. abs //计算输入值的绝对值. acos //返回输入值反余弦值. all / ...

  2. Unity3D Shader 内置函数

    Intrinsic Functions (DirectX HLSL) The following table lists the intrinsic functions available in HL ...

  3. Unity Shader入门精要学习笔记 - 第6章 开始 Unity 中的基础光照

    转自冯乐乐的<Unity Shader入门精要> 通常来讲,我们要模拟真实的光照环境来生成一张图像,需要考虑3种物理现象. 首先,光线从光源中被发射出来. 然后,光线和场景中的一些物体相交 ...

  4. 【Unity Shader学习笔记】Unity基础纹理-法线贴图

    1 高度纹理 使用一张纹理改变物体表面法线,为模型提供更多细节. 有两种主要方法: 1.高度映射:使用一张高度纹理(height map)来模拟表面位移(displacement).得到一个修改后的法 ...

  5. 【Unity Shader】(四) ------ 纹理之法线纹理、单张纹理及遮罩纹理的实现

    笔者使用的是 Unity 2018.2.0f2 + VS2017,建议读者使用与 Unity 2018 相近的版本,避免一些因为版本不一致而出现的问题. [Unity Shader](三) ----- ...

  6. 第四章 开始Unity Shader学习之旅(2)

    目录 1. 强大的援手:Unity提供的内置文件和变量 1.1 内置的包含文件 1.2 内置的变量 2. Unity提供的Cg/HLSL语义 2.1 什么是语义 2.2 Unity支持的语义 2.3 ...

  7. Unity Shader 入门精要学习 (冯乐乐 著)

    第1篇 基础篇 第1章 欢迎来到Shader的世界 第2章 渲染流水线 第3章 Unity Shader 基础 第4章 学习Shader所需的数学基础 第2篇 初级篇 第5章 开始Unity Shad ...

  8. Unity Shader入门精要学习笔记 - 第9章 更复杂的光照

    转载自 冯乐乐的<Unity Shader入门精要> Unity 的渲染路径 在Unity里,渲染路径决定了光照是如何应该到Unity Shader 中的.因此,如果要和光源打交道,我们需 ...

  9. Unity Shader入门精要学习笔记 - 第5章 开始 Unity Shader 学习之旅

    一个顶点/片元 着色器的结构大概如下: Shader "MyShaderName" { Properties { //属性 } SubShader { //针对显卡A的SubSha ...

随机推荐

  1. JavaScript设计模式—单例模式

    单例模式介绍 系统中被唯一使用的,一个类只有一个实例 单例模式的思路是: 一个类能返回一个对象的引用(并且永远是同一个)和一个获得该实例的方法(静态方法,通常使用 getInstance 名称). 那 ...

  2. gitlab+jenkins环境搭建.md

    gitlab+jenkins自动化部署环境搭建 环境说明 系统 主机 IP 安装软件 CentOS 7 study-1 192.168.100.51 gitlab.git CentOS 7 study ...

  3. [USACO08DEC]Patting Heads

    嘟嘟嘟 这题还是比较水的.首先O(n2)模拟显然过不了,那就换一种思路,考虑每一个数对答案的贡献,显然一个数a[i]会对后面的a[i] * 2, a[i] * 3,a[i] * 4……都贡献1,.那么 ...

  4. Hive学习之路 (十)Hive的高级操作

    一.负责数据类型 1.array 现有数据如下: 1 huangbo guangzhou,xianggang,shenzhen a1:30,a2:20,a3:100 beijing,112233,13 ...

  5. Docker实战(四)之Docker数据管理

    在生产环境中使用Docker的过程中,往往需要对数据进行持久化,或者需要在多个容器之间进行数据共享,这必然涉及到容器的数据管理操作. 容器中管理数据主要有两种形式: 数据卷:容器内数据直接映射到本地主 ...

  6. _tcschr和_tcsrchr使用

    好处:是可以不管是用unicode 编码还是其他 ,代码都不用改. C++标准库函数提供了字符和字符串的操作函数,并提供了其UNICODE版本,如: 1._tcschr代替strchr或者wcschr ...

  7. Spring AOP源码分析(一)使用示例

    摘要: 本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 我们知道,使用面向对象编程(OOP)有一些弊端,当需要为多个不具有继承 ...

  8. IE8 下处理select标签高度不居中

    IE8:          Chrome:   同样的代码可是在IE8下select的文字并不是垂直居中. 处理:给select的样式添加padding上下的内边距. 加line-height一点用都 ...

  9. 关于Android

    1:Handle与多线程 Handle是什么?官方说明:handle是Android给我们提供用来更新UI的一套机制,也是一套消息处理的机制.可以看出handle主要就是两个功能,一个是更新UI,另一 ...

  10. VS2010调试和头文件路径设置

    1:VS2010 release 调试C/C++ -> 常规 -> 调试信息格式, 修改为程序数据库(/Zi)C/C++ ->优化 -> 优化,修改为已禁用(/Od)链接器 - ...