学习第六章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. PHP设计模式系列 - 外观模式

    外观模式 通过在必需的逻辑和方法的集合前创建简单的外观接口,外观设计模式隐藏了调用对象的复杂性. 外观设计模式和建造者模式非常相似,建造者模式一般是简化对象的调用的复杂性,外观模式一般是简化含有很多逻 ...

  2. [错误记录]python requests库 Response 判断坑

    在requests访问之后, 我直接判断resp的值, 如下: if resp: do something 发现当Response 为500的时候没有进入if分支, 检查源码,发现Response重写 ...

  3. REST接口设计规范总结

    简介 Representational State Transfer 简称 REST 描述了一个架构样式的网络系统.REST 指的是一组架构约束条件和原则.满足这些约束条件和原则的应用程序或设计就是 ...

  4. 错误检查roswtf

    准备 在你开始本教程之前请确保roscore没在运行. 安装检查 roswtf 可以检查你的ROS系统并尝试发现问题,我们来试看: $ roscd $ roswtf 你应该会看到(各种详细的输出信息) ...

  5. 学习笔记·堆优化$\mathscr{dijkstra}$

    嘤嘤嘤今天被迫学了这个算法--其实对于学习图论来说我内心是拒绝的\(\mathscr{qnq}\) 由于发现关于这个\(\mathscr{SPFA}\)的时间复杂度\(O(kE)\)中的\(k \ap ...

  6. P1353 [USACO08JAN]跑步Running

    题目描述 The cows are trying to become better athletes, so Bessie is running on a track for exactly N (1 ...

  7. 怎么用CIFilter给图片加上各种各样的滤镜_2

    上一篇讲了怎么找到能用的的滤镜和大概怎么去寻找... 这里接着说如何详细地给图片加滤镜效果..前的准备工作... . 1. 在找到想用的滤镜名字之后.须要知道这个滤镜究竟须要什么參数. . 例如以下图 ...

  8. Spring MVC 入门笔记

    主要名词解释 DispatcherServlet 前端控制器  相当于一个转发器 入口: protected void doDispatch(HttpServletRequest request, H ...

  9. 网格布局(GridLayout) 行数与列数

    1.如果网格布局对象未指定具体的“行数”和“列数”,那么它将拥有1行和动态的列数. import java.awt.Button; import java.awt.Frame; import java ...

  10. mysql where语句多条件查询是and和or联合使用bug

    公司项目中有段功能是全局搜索框和下拉列表的联合查询,在联调开发中发现单独用下拉查询是正确的,单独用全局搜索框也是正确的,测试发现是sql语法有问题. 问题截图: 出现问题的核心还是在于搜索框是用于多个 ...