title: unity-shader相关
categories: Unity3d-Shader
tags: [unity, shader]
date: 2017-09-12 10:05:18
comments: false

unity shader 相关记录


相关资料

  • 有很多酷炫shader效果的网站 - https://www.shadertoy.com/view/XllcR4

    • 卡通渲染: http://sorumi.xyz/posts/unity-toon-shader/

      • 不错的卡通渲染: https://github.com/unity3d-jp/UnityChanToonShaderVer2_Project

        • 官网地址: http://unity-chan.com/download/releaseNote.php?id=UTS2_0&lang=en
    • Unite 2018 | 《崩坏3》:在Unity中实现高品质的卡通渲染
      • http://forum.china.unity3d.com/thread-32271-1-1.html
      • http://forum.china.unity3d.com/thread-32273-1-1.html
  • Unity Shader-深度相关知识总结与效果实现(LinearDepth,Reverse Z,世界坐标重建,软粒子,高度雾,运动模糊,扫描线效果) - https://blog.csdn.net/puppet_master/article/details/77489948
  • OpenGL 矩阵变换(讲的太好了~!) - https://blog.csdn.net/lyx2007825/article/details/8792475
  • 3D图形学基础 - https://zhuanlan.zhihu.com/p/27846162
  • 计算机图形学的光学基础&BRDF公式的推导 ( 好文 ) - https://www.bilibili.com/read/cv548776/
  • Unity Shader学习笔记(15)立方体纹理、反射、折射、菲涅尔反射 - http://gad.qq.com/article/detail/38332 ( 底部有一系列的图形学教程 )
  • unity standard shader 详解 : http://geekfaner.com/unity/blog16_UnityStandardShader.html
  • 油管上看到的很好的shader练习: Shaders Laboratory - https://www.youtube.com/channel/UCDk9-aPr8zQzwi4ylnuoJ6w
    • 源码都在这里: http://www.shaderslab.com/shaders.html

感觉不错的仓库

  • https://github.com/techizgit/UnityPlayground
  • https://github.com/QianMo/Awesome-Unity-Shader
  • https://github.com/przemyslawzaworski/Unity3D-CG-programming
  • https://github.com/techizgit/UnityPlayground
  • 次世代海水 - https://github.com/eliasts/Ocean_Community_Next_Gen
  • ShaderToy to Unity HLSL/CG - https://github.com/smkplus/ShaderMan
    • 网页端转换 - https://smkplus.github.io/ShaderMan.io/
    • 油管演示 ShaderToy to Unity

在线画图工具, 数学公式

  • https://www.geogebra.org/graphing (推荐)

    不支持复合公式, 报错

  • https://www.desmos.com/calculator (推荐)

    支持复合公式

  • http://fooplot.com/?lang=zh_hans

  • https://zh.numberempire.com/graphingcalculator.php


todo

  • 特效 及其 shader 研究

  • Unity Shader - 这个人的专栏不错 , 很多 后处理 案例:

    • https://blog.csdn.net/puppet_master/column/info/12790

    • 卡通渲染: http://sorumi.xyz/posts/unity-toon-shader/

      • 不错的卡通渲染: https://github.com/unity3d-jp/UnityChanToonShaderVer2_Project
    • 均值模糊

      • Unity Shader-后处理:简单均值模糊 - https://blog.csdn.net/puppet_master/article/details/52547442 . (工程内的 PostProcBlurJunzhi)
    • 高斯模糊

      • Unity Shader-后处理:高斯模糊 - https://blog.csdn.net/puppet_master/article/details/52783179

        迭代次数越大, 批次越大. 也可以做 毛玻璃 效果 (磨砂)

      • learnopengl-cn - https://learnopengl-cn.readthedocs.io/zh/latest/05 Advanced Lighting/07 Bloom/#_3

    • 景深

      • Unity Shader-后处理:景深 - https://blog.csdn.net/puppet_master/article/details/52819874
    • 径向模糊

      • 屏幕特效之径向模糊 - https://blog.csdn.net/u011047171/article/details/48630227
      • 后处理:径向模糊效果 - https://blog.csdn.net/puppet_master/article/details/54566397
    • 运动模糊

      • 运动模糊
    • 屏幕扭曲

      • Unity Shader-后处理:时空扭曲效果 - https://blog.csdn.net/puppet_master/article/details/71437031
    • UnityShader-BilateralFilter(双边滤波,磨皮滤镜)- https://blog.csdn.net/puppet_master/article/details/83066572

    • Unity Shader-Decal贴花 (Projector原理) - https://blog.csdn.net/puppet_master/article/details/84310361

    • 描边

    • 透视

    • 体积光

      • Unity Shader-GodRay,体积光(BillBoard,Volume Shadow,Raidal Blur,Ray-Marching) - https://blog.csdn.net/puppet_master/article/details/79859678
      • 游戏开发相关实时渲染技术之体积光 - https://zhuanlan.zhihu.com/p/21425792
    • 残影效果实现

      • Unity角色残影特效 - https://blog.csdn.net/qq992817263/article/details/52994907

        这里只针对SkinnedMeshRenderer的网格(也就是带蒙皮的网格)残影,主要原理是根据设定的间隔时间连续的截取当前SkinnedMeshRenderer的网格数据并使用Graphics.DrawMesh画出网格

  • 图形学研究: http://blog.csdn.net/poem_qianmo


渲染管线

下图显示了渲染管线中各个阶段主要完成的工作,蓝色部分代表的是我们可以定义自己的着色器。

在上图中,我们以数组的形式传递3个3D坐标作为渲染管线的输入,用它来表示一个三角形,这个数组叫做顶点数据(Vertex Data);这里顶点数据是几个顶点的集合。每个顶点是用顶点属性(vertex attributes)表示的,它可以包含任何我们希望用的数据,下面我们来看看渲染管线中各个阶段主要完成的工作:

  • 渲染管线的第一个部分是顶点着色器(vertex shader),它把一个单独的顶点作为输入。顶点着色器主要的目的是把3D坐标转为另一种3D坐标(投影坐标),同时顶点着色器允许我们对顶点属性进行一些基本处理。
  • 图元组装(primitive assembly)阶段把顶点着色器的表示为基本图形的所有顶点作为输入,把所有点组装为特定的基本图形的形状;上图中是一个三角形。
  • 图元组装阶段的输出会传递给几何着色器(geometry shader)。几何着色器把基本图形形成的一系列顶点的集合作为输入,它可以通过产生新顶点构造出新的(或是其他的)基本图形来生成其他形状。
  • 细分着色器(tessellation shaders)拥有把给定基本图形细分为更多小基本图形的能力。这样我们就能在物体更接近玩家的时候通过创建更多的三角形的方式创建出更加平滑的视觉效果。
  • 细分着色器的输出会进入光栅化(rasterization)阶段,这里它会把基本图形映射为屏幕上相应的像素,生成供像素着色器(fragment shader)使用的fragment(OpenGL中的一个fragment是OpenGL渲染一个独立像素所需的所有数据。)。在像素着色器运行之前,会执行裁切(clipping)。裁切会丢弃超出你的视图以外的那些像素,来提升执行效率。
  • 像素着色器的主要目的是计算一个像素的最终颜色,这也是OpenGL高级效果产生的地方。通常,像素着色器包含用来计算像素最终颜色的3D场景的一些数据(比如光照、阴影、光的颜色等等)。
  • 在所有相应颜色值确定以后,最终它会传到另一个阶段,我们叫做alpha测试和混合(blending)阶段。这个阶段检测像素的相应的深度(和stencil)值,使用这些来检查这个像素是否在另一个物体的前面或后面,如此做到相应取舍。这个阶段也会查看alpha值(alpha值是一个物体的透明度值)和物体之间的混合(blend)。所以即使在像素着色器中计算出来了一个像素所输出的颜色,最后的像素颜色在渲染多个三角形的时候也可能完全不同。

虽然渲染管线有多个阶段,每个阶段都需要对应的着色器,但其实对于大多数场合,我们必须做的只是顶点和像素着色器,几何着色器和细分着色器是可选的,通常使用默认的着色器就行了。现在的OpenGL中,我们必须定义至少一个顶点着色器和一个像素着色器(因为GPU中没有默认的顶点/像素着色器)。

Unity把每一frame绘制的事件进行了拆分,然后在其中定义了一些点,在这些点处,可以通过command buffer嵌入一些事件(比如设置RT,绘制一些物件等)。比如在延迟渲染中,可以当G-Buffer中绘制完毕后,往里面绘制一些额外物件。通过下图可以看到Unty的渲染顺序,并可以清晰的看到在绿色点标记的地方可以嵌入command buffer去执行自定义的命令:


RenderingMode 渲染模式

渲染模式总共有四种:

渲染模式 意思 适用对象举例 说明
Opaque 不透明 石头 适用于所有的不透明的物体
Cutout 镂空 破布 透明度不是0%就是100%,不存在半透明的区域。
Fade 隐现 物体隐去 与Transparent的区别为高光反射会随着透明度而消失。
Transparent 透明 玻璃 适用于像彩色玻璃一样的半透明物体,高光反射不会随透明而消失。

变量命名规则

以法线为例, 一般会以某个空间为前缀.

// 切线空间下的法线
fixed3 tangentNormal = UnpackNormal(packedNormal);// 世界空间下的法线
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);

顶点法线

  • http://blog.csdn.net/zhw_giser/article/details/11950307

顶点->图元->像素

  • DirectX 11—从空间变换来看3D场景如何转化到2D屏幕 - http://reedhong.lofter.com/post/18b79b_49bf11

UnityShader之空间变换解析

  • http://blog.csdn.net/qq984786645/article/details/73467282
  • https://www.cnblogs.com/X-Jun/p/7241839.html

模板测试

  • http://www.hiwrz.com/2016/07/09/unity/246/
  • http://www.open-open.com/lib/view/open1484468851059.html
  • http://blog.csdn.net/wangdingqiaoit/article/details/52143197

混合

unity 内置的 Unlit-Texture,渲染类型 Opaque (不透明物体),所以不用开 Blend,默认也是关闭了混合( Blend Off)

  • Alpha混合 http://blog.sina.com.cn/s/blog_471132920101d8z5.html

  • Alpha Test和Alpha Blending http://blog.csdn.net/candycat1992/article/details/41599167

    总结一下,就是使用Alpha Test看似更简单,但其实在大多数平台上,相比与Alpha Blending,只有一点小小的性能提升。但是!!!在iOS和某些Android设备上,由于它们使用了PowerVR GPUs,因此Alpha Test的性能消耗反而会更大。因此,一个忠告就是,**尽可能使用Alpha Blending,而不要使用 **Alpha Test。

  • 效果:http://blog.csdn.net/wdsdsdsds/article/details/52535352

  • 透明度测试与透明度混合详解 : https://blog.csdn.net/u013354943/article/details/52797568

屏幕上显示颜色计算规则

语法

Blend Off 不混合
Blend SrcFactor DstFactor SrcFactor是源系数,DstFactor是目标系数
最终颜色 = (Shader计算出的点颜色值 * 源系数)+(点累积颜色 * 目标系数)

属性(往SrcFactor,DstFactor 上填的值, 也就是 系数

解释
one 1
zero 0
SrcColor 源的RGB值,例如(0.5,0.4,1)
SrcAlpha 源的A值, 例如0.6
DstColor 混合目标的RGB值例如(0.5,0.4,1)
DstAlpha 混合目标的A值例如0.6
OneMinusSrcColor (1,1,1) - SrcColor
OneMinusSrcAlpha 1- SrcAlpha
OneMinusDstColor (1,1,1) - DstColor
OneMinusDstAlpha 1- DstAlpha

运算法则示例:

(注:r,g,b,a,x,y,z取值范围为[0,1])

计算方式
(r,g,) * a = (ra, ga, b*a)
(r,g,) * (x,y,z) = (rx, gy, b*z)
(r,g,) + (x,y,z) = (r+x, g+y, b+z)
(r,g,) - (x,y,z) = (r-x, g-y, b-z)

源颜色 为 该次绘制物体颜色, 目标颜色 为 缓冲区颜色

  • Blend SrcAlpha OneMinusSrcAlpha

    最终颜色 = 源颜色 * 源透明值 + 目标颜色*(1 - 源透明值)

    结论:贴图alpha值越大,颜色越偏向贴图;alpha值越小,颜色越偏向混合目标


unity 内置变量

  • http://blog.csdn.net/lzhq1982/article/details/73747162

矩阵

http://ios.jobbole.com/89931/

在线 矩阵计算器 :http://www.yunsuanzi.com/matrixcomputations/solvematrixmultiplication.html

矩阵乘法

A的列 一定要等于 B的行 ( 不能使用交换律 )

正交矩阵

http://www.mashangxue123.com/线性代数/2077705030.html

先来看一下正交矩阵是如何定义的,若方阵M是正交的,则当且仅当M与他的转置矩阵M^T的乘积等于单位矩阵,那么就称矩阵M为正交矩阵.

MTM=IM^{T} M=I MTM=I
在矩阵的逆中我们知道,矩阵的逆和矩阵的乘积为单位矩阵I,由此推理,我们可以知道,如果该矩阵为正交矩阵,那么矩阵的逆和转置矩阵是相等的.

MT=M−1M^{T}=M^{-1} MT=M−1
那么正交矩阵存在的意义是什么呢?其实如果一个矩阵是正交矩阵,那么矩阵的逆和转置矩阵是相等的.转置矩阵是非常简单计算的,而计算矩阵的逆如果使用代数余子式计算是非常的麻烦,所以我们可以直接计算转置矩阵然后直接得到该矩阵的逆.

底下是一些重要的性质:

  • 正交矩阵:
  • 正交、逆、转置 矩阵:http://ios.jobbole.com/89931/

单位矩阵 I

在矩阵的乘法中,有一种矩阵起着特殊的作用,如同数的乘法中的1,这种矩阵被称为单位矩阵。它是个方阵,从左上角到右下角的对角线(称为主对角线)上的元素均为1。除此以外全都为0。
I1=[1],I2=[1001],I3=[100010001],⋯ ,In=[10⋯001⋯0⋮⋮⋱⋮00⋯1]I_{1}=[1], I_{2}=\left[ \begin{array}{cc}{1} & {0} \\ {0} & {1}\end{array}\right], I_{3}=\left[ \begin{array}{ccc}{1} & {0} & {0} \\ {0} & {1} & {0} \\ {0} & {0} & {1}\end{array}\right], \cdots, I_{n}=\left[ \begin{array}{cccc}{1} & {0} & {\cdots} & {0} \\ {0} & {1} & {\cdots} & {0} \\ {\vdots} & {\vdots} & {\ddots} & {\vdots} \\ {0} & {0} & {\cdots} & {1}\end{array}\right] I1​=[1],I2​=[10​01​],I3​=⎣⎡​100​010​001​⎦⎤​,⋯,In​=⎣⎢⎢⎢⎡​10⋮0​01⋮0​⋯⋯⋱⋯​00⋮1​⎦⎥⎥⎥⎤​


正交矩阵

但事实上,一个坐标系能用任意3个基向量定义,当然这三个基向量要线性无关(也就是不在同一平面上),

如果这三个基向量(归一化单位向量) 相互垂直 ,那么构成的矩阵是一个正交矩阵

正交矩阵 求 逆矩阵

// 求 TBN 矩阵的逆矩阵,因为 TBN 矩阵由三个互相垂直的单位向量组成,所以它是一个正交矩阵
// 正如前面所说,正交矩阵的逆矩阵等于它的转置,所以无需真的求逆矩阵

效果

  • Unity Shader-非主流纹理采样研究(流光,溶解,隐身效果)

    • http://blog.csdn.net/puppet_master/article/details/76359838
  • Unity Shader-热空气扭曲效果 (GrabPass 抓屏)

    • http://blog.csdn.net/puppet_master/article/details/70199330
  • Unity Shader-遮挡处理(X-Ray,遮挡描边,遮挡半透,遮挡溶解)

    • http://blog.csdn.net/puppet_master/article/details/73478905
    • x-ray : http://www.cnblogs.com/lijiajia/p/6861432.html
  • 复古风格

    • http://www.cnblogs.com/lijiajia/p/6861409.html

切线空间

unity-shader-切线空间.md

使用 法线贴图 是因为在 低模 下想获得 高模 凹凸表面光照效果。(也就是面数不够,法线来凑,基于面的法线建立一个 虚拟坐标系 ,通过 法线贴图 在面上 加多点法线 )

由于需要将同一份法线纹理贴到不同模型的表面,或者同一个模型中不同角度的表面,那么当初生成这份法线纹理时所使用的面元角度(所谓模型local坐标系),就无法适用在其他角度的面元上。所以,人们不记录当时用模型坐标系生成的法线,而使用z轴与该点所在面元的法线平行的一个虚拟的相对坐标系来记录该点法线在该坐标系中的x/y/z的值,相当于记录了该点法线与所在平面法线之间的相对关系,而不是记录绝对坐标。由于点的法向量基本不会偏移面法向量太多,也就是凹凸程度一般不会太夸张,所以最终的法向量值中,z的值总是比x和y的大一些(也就是说 点法向量 与平面的 的夹角一般会大于45度),用(法向量值+1)/2转换为RGB后,就成了蓝色为主的色调了。

经过这样记录下来的法向量,贴到哪个模型表面,就按照当前表面的法向量,计算出该点法向量在世界坐标空间中的值,然后计算光照就可以了。

但是有个问题,看下图,竖轴同样是面法向量,但是有多种不同的x和y轴组合,每种组合生成的点法向量是不一致的,所以需要规定一套固定的x和y轴,大家遵守同样的规则。怎么规定呢?就用纹理的uv坐标来定。具体做法是,取该点所在的三角形的三个顶点P1.P2.P3的纹理U和V坐标,然后x轴的方向就是P3指向P1,又称T轴;y轴方向是P3指向P2,又称B轴。 (完)

参考资料

  • https://www.zhihu.com/question/23706933
  • 切线空间(Tangent Space) 的计算与应用
  • http://docs.cryengine.com/display/SDKDOC4/Tangent+Space+Normal+Mapping
  • http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-13-normal-mapping/

法线向量 使用

参考:https://blog.csdn.net/u013354943/article/details/52779991

1. 切线空间下使用 (常用,比较简单)
  • 直接使用 顶点的 法线normal切线tangent叉乘 算出 副切线binormal,然后三个向量 构建一个 切线空间 的 矩阵,然后将 世界空间 下的 光向量lightDir 和 观察向量viewDir。全过程都在 顶点着色器 中进行
2. 世界空间下使用
  • 先在 顶点着色器 中,算出 世界空间 下的顶点 法线worldNormal切线worldTangent , 叉乘算出 副切线worldBinormal ,然后 构建 切线空间到世界空间的变换矩阵,
  • 然后在 片段着色器 中,将 切线向量 变换 到 世界空间下

法线贴图

  • http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-13-normal-mapping/

  • 法线纹理(Normal Mapping)的实现细节 https://blog.csdn.net/candycat1992/article/details/41605257

  • unity中 法线纹理 设置


通过矩阵变换空间

参考 《Unity Shader入门精要》 4.8.1 和 4.9.2

v2f vert(a2v v) {v2f o;// Transform the vertex from object space to projection space// 转换顶点 从 模型空间 到 世界空间o.pos = UnityObjectToClipPos(v.vertex);// Transform the normal from object space to world space// 转换法线 从 模型空间 到 世界空间o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);// o.worldNormal = UnityObjectToWorldNormal(v.normal);// o.worldNormal = mul((float3x3)IT_Object2World, v.normal);// 法线是向量, 所以和顶点变换有所不同. 可以参考 : https://forum.unity.com/threads/_object2world-or-unity_matrix_it_mv.112446/// 转换法线 从 世界空间 到 模型空间float3 objNormal = mul((float3x3)unity_WorldToObject, o.worldNormal)// objNormal 等价与 v.normal // Transform the vertex from object spacet to world space// 转换顶点 从 模型空间 到 世界空间o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;// 另一种变换 法线 从 模型空间 到 世界空间,其实就是 mul(v.normal, (float3x3)unity_WorldToObject); 的运算展开// o.worldNormal = normalize(unity_WorldToObject[0].xyz * v.normal.x + unity_WorldToObject[1].xyz * v.normal.y + unity_WorldToObject[2].xyz * v.normal.z);return o;
}

解析

变换某个分量

以变换 顶点模型空间 -> 剪裁空间 为例. 以下三种方式等价

v2f o;
// 方式一
// o.pos = UnityObjectToClipPos (v.vertex);// 方式二
// float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
// o.pos = mul(UNITY_MATRIX_VP, worldPos);// 方式三
float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
o.pos.x = mul(UNITY_MATRIX_VP[0], worldPos);
o.pos.y = mul(UNITY_MATRIX_VP[1], worldPos);
o.pos.z = mul(UNITY_MATRIX_VP[2], worldPos);
o.pos.w = mul(UNITY_MATRIX_VP[3], worldPos);

踩坑

  • 顶点 变换, 要用 float4 类型, 四个分量. 因为使用到平移, 用的矩阵是 float4x4.

    法线 变换, 用 float3 类型, 三个分量, 因为法线是向量, 不存在平移的概念, 所以用的矩阵是 float3x3.


坐标系

  • Unity 中的坐标系 https://blog.csdn.net/ronintao/article/details/52136673

齐次坐标

  • 齐次坐标(Homogeneous Coordinate)的理解 - https://blog.csdn.net/winbobob/article/details/38829001

标准化设备坐标 ( NDC )

  • https://learnopengl-cn.readthedocs.io/zh/latest/01 Getting started/08 Coordinate Systems/

OpenGL希望在所有顶点着色器运行后,所有我们可见的顶点都变为标准化设备坐标(Normalized Device Coordinate, NDC)。也就是说,每个顶点的x,y,z坐标都应该在-1.0到1.0之间,超出这个坐标范围的顶点都将不可见。我们通常会自己设定一个坐标的范围,之后再在顶点着色器中将这些坐标转换为标准化设备坐标。然后将这些标准化设备坐标传入光栅器(Rasterizer),再将他们转换为屏幕上的二维坐标或像素。

将坐标转换为标准化设备坐标,接着再转化为屏幕坐标的过程通常是分步,也就是类似于流水线那样子,实现的,在流水线里面我们在将对象转换到屏幕空间之前会先将其转换到多个坐标系统(Coordinate System)。将对象的坐标转换到几个过渡坐标系(Intermediate Coordinate System)的优点在于,在这些特定的坐标系统中进行一些操作或运算更加方便和容易,这一点很快将会变得很明显。对我们来说比较重要的总共有5个不同的坐标系统:

  • 局部空间(Local Space,或者称为物体空间(Object Space))
  • 世界空间(World Space)
  • 观察空间(View Space,或者称为视觉空间(Eye Space))
  • 裁剪空间(Clip Space)
  • 屏幕空间(Screen Space)

这些就是我们将所有顶点转换为片段之前,顶点需要处于的不同的状态。

标准化设备坐标是一个x、y和z值在-1.0到1.0的一小段空间。任何落在范围外的坐标都会被丢弃/裁剪,不会显示在你的屏幕上。下面你会看到我们定义的在标准化设备坐标中的三角形(忽略z轴, z轴有深度转化而来):

后处理中的ndc坐标构建

//使用宏和纹理坐标对深度纹理进行采样,得到深度值
float d = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth);
//构建当前像素的NDC坐标,xy坐标由像素的纹理坐标映射而来,z坐标由深度值d映射而来
float4 H = float4(i.uv.x * 2 - 1, i.uv.y * 2 - 1, d * 2 - 1, 1); // 将屏幕坐标(0, 1) 转到ndc坐标(-1, 1)
// gl 的 Zndc 是 -1~1 之间, 所以 d*2-1
// dx 的 Zndc 是 0~1 之间, 所以直接用 d 即可

将顶点从 view 坐标系转换到 NDC 下

这里面包含了两个步骤,将 view坐标系 下的顶点乘以透视矩阵,转换到 Clip坐标系,得到 Clip坐标,

(xclipyclipzclip)=Mprojection⋅(xeyeyeyezcyeweye)\left( \begin{array}{l}{x_{clip}} \\ {y_{clip}} \\ {z_{clip}}\end{array}\right)=M_{p r o j e c t i o n} \cdot \left( \begin{array}{c}{x_{e y e}} \\ {y_{e y e}} \\ {z_{c y e}} \\ {w_{e y e}}\end{array}\right) ⎝⎛​xclip​yclip​zclip​​⎠⎞​=Mprojection​⋅⎝⎜⎜⎛​xeye​yeye​zcye​weye​​⎠⎟⎟⎞​
然后统一除以w,得到NDC 坐标。
(xndcyndczndc)=(xclip/wclipyclip/wclipzclip/wclip)\left( \begin{array}{l}{x_{n d c}} \\ {y_{n d c}} \\ {z_{n d c}}\end{array}\right)=\left( \begin{array}{l}{x_{c l i p} / w_{c l i p}} \\ {y_{c l i p} / w_{c l i p}} \\ {z_{c l i p} / w_{c l i p}}\end{array}\right) ⎝⎛​xndc​yndc​zndc​​⎠⎞​=⎝⎛​xclip​/wclip​yclip​/wclip​zclip​/wclip​​⎠⎞​


ddx, ddy 函数

可以参考:

  • shader中ddx/ddy偏导数的原理和简单应用 - http://blog.sina.com.cn/s/blog_7cb69c550102xvog.html
  • Unity3d ddx ddy 法线 - http://www.dreamfairy.cn/blog/2016/06/15/unity3d-ddx-ddy-normal/

ddx,ddy 只能在 Fragment Shader下使用, 因为它是用来计算相邻像素的某些属性值之差. ( 属性值比如 worldPos )

求出法线的实例. 利用 worldPos 的 x,y 之差查, 得到两个矢量, 通过 叉乘 得到垂直于这两个矢量组成面的 第三个矢量, 也就是法线.

v2f vert (appdata v)
{v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.worldPos = mul(unity_ObjectToWorld, v.vertex);return o;
}fixed4 frag (v2f i) : SV_Target
{float3 dx = ddx(i.worldPos);float3 dy = ddy(i.worldPos);float3 normal = normalize(cross(dx, dy));normal = normal * 0.5 + 0.5;return fixed4(normal,1.0);
}

那么ddx,ddy 可以用在哪里呢。

  1. 计算出该像素的法线
  2. 计算mipmap level 计算当前纹理在屏幕分辨率下的大小是否小于某个mipmap等级的纹理大小,是的话就切换纹理
  3. 视差贴图 从视野方向计算像素挤出的位置的uv值

OpenGL中的坐标变换、矩阵变换

  • https://blog.csdn.net/iispring/article/details/27970937

六种常见坐标系:

  1. Object or model coordinates(模型坐标系)
  2. World coordinates(世界坐标系)
  3. Eye (or Camera) coordinates(视坐标系)
  4. Clip coordinates(裁剪坐标系)
  5. Normalized device coordinates(归一化设备坐标系)
  6. Window (or screen) coordinates(屏幕坐标系)

剪裁坐标

在我们来解释一下这个坐标系为什么叫做“裁剪坐标系”,看看哪里来的“裁剪”二字:

需要特别注意的是,模型坐标系、世界坐标系、视点坐标系中的第四个分量w都是1,但是经过了投影变换之后的坐标的w分量不再为1,这时候就可以发挥w分量的作用了。在我们手动编程计算完gl_Position之后,进入GPU自身的流水管线,GPU会根据裁剪坐标gl_Position中 xyz分量 与 w分量 绝对值的大小进行比较进行裁剪。具体的过程是:GPU依次将gl_Position中x、y、z的绝对值与w的绝对值分别比较,只要有一个分量的绝对值大于w的绝对值 (也就是 Xclip/ Wclip 的值 >1 或 <-1 ) ,GPU就认为该点不在视景体内,就会被裁减掉,也就是说裁剪的过程是GPU自己进行的,没有被裁减掉的坐标 xyz分量 的绝对值都小于 w分量 的绝对值 (也就是 -1< Xclip/ Wclip 的值 <1 ) 。所以现在应该知道经过投影之后的坐标是为了让GPU进行裁剪用的,所以才叫做“裁剪坐标”。


线性空间与GAMMA校正

  • http://geekfaner.com/unity/blog1_gamma.html

Gamma计算很简单,只是个power而已,也就是:
Color out=Color inγ\text { Color }_{out}=\text { Color }_{i n}^{\gamma}  Color out​= Color inγ​
其中的γ就是用来校正的gamma值。

可惜,现实是残酷的,显示器的gamma为2.2,所以如果相机仍然是线性的,那么结果就会变成:

这样在显示器上看到的就会有明显的色彩失真。解决方法是把相机的gamma设成1/2.2,这样两次调整之后又能得到真实场景的色彩了:

对渲染的意义

前面讲的输入是对相机拍的照片而言。而对渲染来说,情况又如何呢?渲染中用到的光照都是在线性空间的。因为在设计光照的时候都是认为1的亮度是0.5的2倍。光照如此,texture又如何呢?渲染中用到的 texture一般有两个来源,一个是照片,一个是artist手工画的。前文提到了,照片是gamma = 1/2.2的。一般图象处理软件也都是在gamma空间工作的,所以artist画的图一般也可以认为是gamma = 1/2.2的。所以,我们在pixel shader常可以见到这样的代码:

float4 diff = tex2D(diffuse_texture, uv);
return diff \* max(0, dot(light_dir, normal));

这样的代码对吗?不对也对。

说其不对,是因为这里没显式地做gamma校正。做校正的话应该是这样的:

float4 diff = pow(tex2D(diffuse_texture, uv), 2.2f);
return pow(diff \* max(0, dot(light_dir, normal)), 1 / 2.2f);

也就是说,gamma校正的过程就是把输入的texture都转换到线性空间,并把输出的调整到gamma = 1/2.2的空间。

总结

总之,计算 都要发生在 线性空间,所以输入和输出需要进行gamma校正。最佳选择是采用sRGB格式,这样pow是 硬件内自动实现 ,速度更快,代码也简单。鉴于目前很多texture的数据是gamma = 1/2.2的,而纹理格式却被错误地标记成没有sRGB的,所以需要修改它们的格式标记,并重新建立mipmap。

贴图格式是sRGB, 也就是该贴图是gamma空间的.


HDR

  • https://learnopengl-cn.readthedocs.io/zh/latest/05 Advanced Lighting/06 HDR/

泛光 bloom

  • https://learnopengl-cn.readthedocs.io/zh/latest/05 Advanced Lighting/07 Bloom/

下面这几步就是泛光后处理特效的过程,它总结了实现泛光所需的步骤。

首先我们需要根据一定的阈限提取所有明亮的颜色。我们先来做这件事。


体积光

挤出顶点

参考: https://blog.csdn.net/puppet_master/article/details/79859678

原理 : 顶点阶段, 计算出哪些是背光, 哪些是受光, 让受光的部分的顶点沿着光的方向挤出. (为啥是受光? 因为受光部分离光比较近, 比较早衰减). 背光部分的顶点则不处理. 这样在光栅化后就能差值形成一个沿光方向延伸的模型.

后处理

类似 bloom 效果, 只是把 高斯模糊 部分换成了 径向模糊

  1. 提取亮色部分 rt1
  2. 将 rt1 做 径向模糊 rt2
  3. 将 rt2 与 原图 叠加

GPU和GLSL并不擅长优化循环和分支

然而事实上,你的GPU和GLSL并不擅长优化循环和分支。这一缺陷的原因是GPU中着色器的运行是高度并行的,大部分的架构要求对于一个大的线程集合,GPU需要对它运行完全一样的着色器代码从而获得高效率


注意问题

矩阵行列优先级

在 CG 中, 对 float4x4 等类型的变量是 按行有限 的方式进行填充。(入门精要 4.9.2)

单位精度

类型 精度
float 最高精度。32位存储
half 中等精度。16位存储,范围 -60 000 ~ +60 000
fixed 最低精度。11位存储,范围 -2.0 ~ +2.0 (常用来存储颜色值(0~1之间))

参考 : Cg/HLSL中的数据类型 - https://zhuanlan.zhihu.com/p/48530294

  • float

    高精度类型,32位,通常用于世界坐标下的位置,纹理UV,或涉及复杂函数的标量计算,如三角函数、幂运算等。

  • half

    中精度类型,16位,数值范围为[-60000,+60000],通常用于本地坐标下的位置、方向向量、HDR颜色等。

  • fixed

    低精度类型,11位,数值范围为[-2,+2],通常用于常规的颜色与贴图,以及低精度间的一些运算变量等。

在PC平台不管你Shader中写的是half还是fixed,统统都会被当作float来处理。half与fixed仅在一些移动设备上有效。
比较常用的一个规则是,除了位置和坐标用float以外,其余的全部用half。主要原因也是因为大部分的现代GPU只支持32位与16位,也就是说只支持float和half,不支持fixed。


特殊技巧

替代 if else 判断

可以参考: unity-shader-GPU优化_step函数替代if-else - https://blog.csdn.net/yangxuan0261/article/details/89852627

GPU 中尽量减少使用 if else 判断逻辑,执行消耗非常大,可以使用以下方式替代

  fixed4 frag(v2f i) : SV_Target{float factor = i.objPos.x - _DissolveThreshold;fixed3 color = tex2D(_MainTex, i.uv).rgb;fixed4 retClr;//方式a, 方式a 与 方式b 等价, 但 方式b 性能更好//if (i.objPos.x > _DissolveThreshold) {//   retClr = _DissolveColor;//} else {//    retClr = _DissolveColor;//}//方式bfixed lerpFactor = saturate(sign(i.objPos.x - _DissolveThreshold));//fixed lerpFactor = step(_DissolveThreshold, i.objPos.x); // 与上一行等价retClr = lerpFactor * _DissolveColor + (1 - lerpFactor) * fixed4(color, 1);//retClr = lerp(fixed4(color, 1), _DissolveColor, lerpFactor); // 与上一行代码等价return retClr;}

sign 约束 值 只能是 -1, 0, 1

saturate 约束值 只能是 [0, 1]

step(a, b) 表示 如果 a <= b,返回 1 ;否则,返回 0 。

所以只要 i.objPos.x > _DissolveThreshold,就取 颜色 _DissolveColor


几何意义解析

  • 点乘(又名 点积,dot):dot(a, b) 求出 向量 a、b夹角的 余弦值,值越大,夹角越小
  • 叉乘(又名 叉积,cross): cross(a, b) 求出 与向量 a、b组成平面 垂直的 c 向量
  • 变换,mul(A, n) 用 矩阵A 将 n 向量变换 到 A空间 下。(A是一个坐标系,可能是 3x3坐标系, 可能是 4x4 齐次坐标系) ,等价与 mul(n, A的逆矩阵) (矩阵相乘不满足交换律)

专栏

  • http://blog.csdn.net/puppet_master/article/category/6441122
  • http://blog.csdn.net/column/details/unityshadertutorial.html

_Time 的单位

名称 类型 说明
_Time float4 t 是自该场景加载开始所经过的时间,4个分量分别是 (t/20, t, t2, t3)
_SinTime float4 t 是时间的正弦值,4个分量分别是 (t/8, t/4, t/2, t)
_CosTime float4 t 是时间的余弦值,4个分量分别是 (t/8, t/4, t/2, t)
unity_DeltaTime float4 dt 是时间增量,4个分量的值分别是(dt, 1/dt, smoothDt, 1/smoothDt)

XX_TexelSize

XX纹理的像素相关大小width,height对应纹理的分辨率,x = 1/width, y = 1/height, z = width, w = height

也就是 : Vector4(1 / width, 1 / height, width, height)


后处理

后处理 需要注意贴图的平台差异

//dx中纹理从左上角为初始坐标,需要反向(在写rt的时候需要注意)
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)o.uv2.y = 1 - o.uv2.y;
#endif

默认传 _MainTex 给 shader

要注意写法

Shader "test/PostProcCircle"  {Properties {_MainTex("Base (RGB)", 2D) = "white" {} // 1. 要声明一个 暴露 的参数, 不然会是一个全灰色的图}CGINCLUDE#include "UnityCG.cginc"sampler2D _MainTex; // 2. 声明 uniform 变量

运动模糊

相关资料

  • Unity Shader实现运动模糊(一) : 摄像机运动产生模糊 - https://blog.csdn.net/h5502637/article/details/84986197

运动模糊是个经常会用到的效果,常见的实现步骤是:

  1. 对深度纹理进行采样,取得当前片元的深度信息
  2. 根据深度信息建立当前片元的NDC空间的坐标curNDCPos
  3. 把curNDCPos乘以当前VP矩阵的逆矩阵(即View*Projection)-1,得到当前片元的世界空间坐标WorldPos
  4. 把WorldPos乘以上一帧的VP矩阵(即View*Projection),得到上一帧在裁切空间中的位置 lastClipPos
  5. 把lastClipPos除以其w分量,得到NDC空间位置lastNDCPos
  6. 用当前片元NDC空间位置 减去 上一帧NDC空间位置(即 curNDCPos-lastClipPos),得到速度的方向speed
  7. 沿speed方向进行多次采样,求出平均值作为当前片元的颜色

法线的变换

参考 :

  • UNITY_MATRIX_IT_MV[Matrix] - https://blog.csdn.net/cubesky/article/details/38682975
  • https://forum.unity.com/threads/_object2world-or-unity_matrix_it_mv.112446/

官方的解释

  • MV transforms points from object to eye space
  • IT_MV rotates normals from object to eye space

And similarly:

  • Object2World transforms points from object to world space
  • IT_Object2World (which, as you point out, is the transpose of World2Object) rotates normals from object to world space

If it is orthogonal, the upper-left 3x3 of Object2World will be equal to that of IT_Object2World, and so will also rotate normals from object to world space.

上面这里很好的描述了UNITY_MATRIX_IT_MV的使用场景,专门针对法线进行变换。但是为什么法线的变换和定点不一样呢?让我们来看一篇推导的文章。

注:之所以法线不能直接使用UNITY_MATRIX_MV进行变换,是因为法线是向量,具有 方向,在进行空间变换的时候,如果发生非等比缩放,方向会发生偏移。为什么呢?拿上面的例子来说,我们可以简单的把法线和切线当成三角形的两条边,显然,三角形在空间变换的时候,不管是平移,还是旋转,或者是等比缩放,都不会变形,但是如果非等比缩放,就会发生拉伸。所以法线和切线的夹角也就会发生变化。(而切线在变换前后,方向总是正确的,所以法线方向就不正确了)。

结论 : 法线的变换需要用 xxx_IT

shader示例1

// 变换 法线 从 模型空间 -> 观察空间
// 方式一
// float3 worldNorm = UnityObjectToWorldNormal(v.normal).xyz;
// float3 viewNormal = mul((float3x3)UNITY_MATRIX_V, worldNorm); // 将法线转到观察空间下, 因为matcap贴图是摄像机看到的贴图
// o.uv.zw = viewNormal.xy; // 转换法线值为贴图值// 方式二
float3 viewNormal = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal.xyz);
o.uv.zw = viewNormal.xy;// 方式三
// o.uv.z = mul(UNITY_MATRIX_IT_MV[0], v.normal);
// o.uv.w = mul(UNITY_MATRIX_IT_MV[1], v.normal);

shader示例2 - 切线空间

v2f2 vert2 (appdata2 v)
{v2f2 o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);float3 worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;o.lightDir = UnityWorldSpaceLightDir(worldPos);o.viewDir = UnityWorldSpaceViewDir(worldPos);// 构建 法线 从 切线空间 到 世界空间 的 变换矩阵(三个向量)fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w;o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z); // 顺便把 世界坐标 也存在这里return o;
}fixed4 frag2 (v2f2 i) : SV_Target
{//获得世界空间中的坐标float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);//计算光照和视角方向在世界坐标系中fixed3 worldLightDir = normalize(i.lightDir);fixed3 worldViewDir = normalize(i.viewDir);fixed4 packedNormal = tex2D(_NormalTex, i.uv);fixed4 tex = tex2D(_MainTex, i.uv);// 法线图 解包 后 的 切线空间的法线向量fixed3 tangentNormal = UnpackNormal(packedNormal);// 方式一// 转换 法线向量 从 切线空间 到 世界空间, 等价于 下面注释部分// fixed3 worldNormal = normalize(half3(dot(i.TtoW0.xyz, tangentNormal), dot(i.TtoW1.xyz, tangentNormal), dot(i.TtoW2.xyz, tangentNormal)));// 方式二fixed3 worldNormal = normalize(half3(mul(i.TtoW0.xyz, tangentNormal), mul(i.TtoW1.xyz, tangentNormal), mul(i.TtoW2.xyz, tangentNormal)));// 方式三// 构建 转换矩阵// float3x3 worldNormalMatrix = float3x3(i.TtoW0.xyz, i.TtoW1.xyz, i.TtoW2.xyz);// fixed3 worldNormal = normalize(mul(worldNormalMatrix, tangentNormal));fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * tex.rgb;//漫反射fixed3 diffuse = _LightColor0.rgb * tex.rgb * saturate(dot(worldNormal, worldLightDir));//Blinn-Phong高光光照模型,相对于普通的Phong高光模型,会更加光fixed3 halfDir = normalize(worldLightDir + worldViewDir);fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal, halfDir)), _Gloss);//边缘颜色,对于法线和观察方向,只要在同一坐标系下即可fixed dotProduct = 1 - saturate(dot(worldNormal, worldViewDir));fixed3 rim = _RimColor.rgb * pow(dotProduct, 1 / _RimPower);fixed4 maskCol = tex2D(_MaskTex, i.uv + float2(0, _Time.y * _MoveDir));return fixed4(ambient + diffuse + specular + rim * maskCol.rgb, 1);// return fixed4(ambient + diffuse, 1);
}

ComputeScreenPos 屏幕空间位置

参考:

  • ComputeScreenPos 详解 - https://chengkehan.github.io/ComputeScreenPos.html
  • Unity Shader中的ComputeScreenPos函数 - https://www.jianshu.com/p/df878a386bec

获取像素在屏幕空间的位置

  • 内置 ComputeScreenPos 实现

    inline float4 ComputeNonStereoScreenPos(float4 pos) {float4 o = pos * 0.5f;o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w;o.zw = pos.zw;return o;
    }inline float4 ComputeScreenPos(float4 pos) {float4 o = ComputeNonStereoScreenPos(pos);
    #if defined(UNITY_SINGLE_PASS_STEREO)o.xy = TransformStereoScreenSpaceTex(o.xy, pos.w);
    #endifreturn o;
    }
    
  • 内置 tex2Dproj 实现

    half4 tex2Dproj(sampler2D s, in half4 t)        { return tex2D(s, t.xy / t.w); }
    

简单的使用

v2f vert (appdata v) {v2f o;o.vertex = UnityObjectToClipPos(v.vertex);// o.uv = TRANSFORM_TEX(v.uv, _MainTex);o.projPos = ComputeScreenPos(o.vertex); // 参数是剪裁空间下的坐标UNITY_TRANSFER_FOG(o,o.vertex);return o;
}fixed4 frag (v2f i) : SV_Target {fixed4 col = tex2D(_MainTex, i.projPos.xy / i.projPos.w);// fixed4 col = tex2Dproj(_MainTex, i.projPos);return col;
}

一般采样贴图是都是顶点中的uv值, 是个固定值的, 所以不会随着顶点位置的变化而变化, 就会造成拉伸.

可以做图片不被拉伸的效果, 因为采样的 uv 值是 0-1, 所以只用当四个顶点完后只能覆盖屏幕四个角落时, 才会显示出完整的图片.
因为 mesh 的所有的顶点在屏幕空间下的值都会在 (0, 0) 到 (1, 1) 区间内.


表面着色器

参考:

  • 官方各种示例 - https://docs.unity3d.com/Manual/SL-SurfaceShaderExamples.html
# 指定 表面函数 和 光照函数
#pragma surface surf Toon
struct Input {float2 uv_MainTex;float2 uv_Bump;};
void surf (Input IN, inout SurfaceOutput o) {...}
half4 LightingToon(SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten) {...}

雾效

unity中使用雾效的姿势. 主要是四个地方, 加下面注释处.

Pass
{CGPROGRAM#pragma multi_compile_fog // 雾效编译指令struct v2f{float2 uv : TEXCOORD0;UNITY_FOG_COORDS(1) // 雾效数据字段, 1 为寄存器语义 TEXCOORD1, 完整展开 - float fogCoord : TEXCOORD1float4 vertex : SV_POSITION;};v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);UNITY_TRANSFER_FOG(o,o.vertex); // 将 剪裁空间 下 顶点 的 z 值 存进 fogCoord 字段中return o;}fixed4 frag (v2f i) : SV_Target{// sample the texturefixed4 col = tex2D(_MainTex, i.uv);UNITY_APPLY_FOG(i.fogCoord, col); // 最终颜色加上雾效return col;}
}

shader 可视化编辑

比较热门的两款 可视化 插件 Amplify Shader vs Shader Forge

Amplify Shader vs Shader Forge

  • https://www.reddit.com/r/gamedev/comments/802tuy/amplify_shader_vs_shaderforge_vs_unity_20181/
  • https://www.zhihu.com/question/266358345
  • https://www.zhihu.com/question/59642795

Shader Forge 貌似更新太慢等很多抱怨

Amplify Shader 的可视化 和 ue4 的材质编辑器 很相似,编辑出来的是 surface着色器 的源码

Shader Forge 编辑出来的是 vertex着色器与fragment着色器 的源码


优化

  • gpu 并不擅长 循环和判断 语句

  • 分量计算

    也就是说
    float4 f = float(1,1,1,1);
    f = float4(f.x +=1, f.y +=1, f.z +=1, f.w += 1);
    算了4系分量
    没有
    f += 1;
    的效率高
    

常见问题

(1) TRANSFORM_TEX是做什么的

(2)float4 _MainTex_ST 中的_MainTex_ST变量也没有用到,为啥非要声明一下?

答:

(1)简单来说,TRANSFORM_TEX主要作用是拿顶点的uv去和材质球的tiling和offset作运算, 确保材质球里的缩放和偏移设置是正确的。 (v.texcoord就是顶点的uv)

下面这两个函数是等价的。

o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);

o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;

所以使用他有两个前提:

\1. #include “UnityCG.cginc”

\2. 定义name##_ST

(2)而_MainTex_ST的ST是应该是SamplerTexture的意思 ,就是声明_MainTex是一张采样图,也就是会进行UV运算。 如果没有这句话,是不能进行TRANSFORM_TEX的运算的。_MainTex_ST.xy为 下图中的Tiling,zw为下图中的offset.

如果Tiling 和Offset你留的是默认值,即Tiling为(1,1) Offset为(0,0)的时候,可以不用

o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);

换成o.uv = v.texcoord.xy;也是能正常显示的;相当于Tiling 为(1,1)Offset为(0,0),但是如下图自己填的Tiling值和Offset值就不起作用了

clip 函数

  // alpha testclip (o.Alpha - _CutOff);

clip(x) : 如果输入向量中的任何元素小于0,则丢弃当前像素。

也就是说,是通过 Alpha 值 减去 指定的 _CutOff ,也就是,Alpha 值 低于 _CutOff 的片段都会被丢弃。

根据书上所说,这次学习的 透明裁剪着色器,因为要逐像素判断 Alpha 是否小于 _CutOff ,所以是比较消耗 GPU的,所以在手机并不推荐

但是在工作中,会发现,其实很大程度上都是一个度的问题。

像这中溶解效果,用在副本里面,不可能几十个怪同时死亡而同时显示溶解的效果的。

所以并没有什么在手机上就不能用这种事情。想用就用。

SV_Target、SV_POSITION

SV其实是system-value(系统数值)的意思,需要注意的是SV开头的语义考虑到扩平台的问题了,所以能用SV开头的语义的尽量使用SV开头的语义,免去跨平台的烦恼

http://blog.csdn.net/a958832776/article/details/70847033

http://blog.csdn.net/zhao_92221/article/details/46797969

Q: Duplicated input semantics can’t change type, size or layout. (TEXCOORD1)

重复使用了 TEXCOORD1 语义,可能类型 UNITY_FOG_COORDS(1) 这样的unity宏里使用了,所以改成 TEXCOORD2\3\4\5 即可

Q: 法线图偏蓝紫色 (rgb(0.5,0.5,1))

法线贴图每个像素存的都是每点(当然这些点是离散化的)的局部法向量坐标,所以没有扰动法向量的时候,图片全部像素应该是rgb(0.5,0.5,1) 大概就是蓝紫色那样 (假设z轴就是法向)。因为单位法向量的xy分量取值在(-1,1)但是颜色分量取值在(0,1),所以把这个区间映射一下就会发现未被扰动的向量(0,0,1)会被映射成(0.5,0.5,1)这个偏蓝紫色的颜色

映射公式: pixel = (normal + 1) / 2

通常情况下,美工会做出一张使用的RGB三个通道用来存放法线的XYZ三个轴向的坐标的法线贴图。但是Unity在导入法线贴图的时候会自动将法线贴图压缩成 DXT5nm 格式,这个格式的好处是 它只使用AG(透明和绿色)两个通道来存放两个轴向的坐标值。而Z轴向的坐标值由于是 单位坐标 可以通过 1 减去另外前两个轴向坐标的 平方和 来得到(已知2个分量,可求第三个),从而可以以同样的容量存放更大尺寸的法线贴图,我们都知道图片的颜色通道存的都是非负数(法线贴图生成的时候已经把[-1,1]压缩为[0-1]),而我们的三维空间是[-1,1],所以我们要把它解析放大一下,方法就是对对应的颜色通道值乘以2再减1。这就能存储游戏物体所有的法线信息了。例如,一个RGB值为(0.5,0.5,1)或#8080ff(16进制)的颜色向量,它所存储法线向量值为(0,0,1),代表该图向上的法向量,即模型没有凹凸现象。对比本页面之前的模型平面区域,您就会发现这种颜色基调。

参考 : 法线贴图详解之三----凹凸,法线以及高度图的区别及法线贴图蓝紫色的原因 - http://manew.com/thread-90210-1-1.html

因此, 如果切线空间下的法线 和 顶点 ( 插值后的) 法线 重合 (平行) , 则这该点像素值为 ( 0.5, 0.5, 1), 也就是对应的法线值 ( 0, 0, 1), 也就是切线空间坐标系的z轴, 也就是顶点的法线.

Q: worldNorm.xy * 0.5 + 0.5 是几个意思

等价与 上面那个问题的 映射公式,都是将 法线值转为 像素值

会使用到这个公式 一般都是 使用了 fixed3 normals = UnpackNormal(tex2D(_BumpMap, i.uv_bump)); 将法线值 从 法线图 解包后

pixel = (normal + 1) / 2
//等价与
pixel = (normal * 0.5 + 1 * 0.5//推导出
normal = pixel * 2 - 1

Q: unity_ObjectToWorld 和 unity_WorldToObject 是互逆矩阵?

对,所以用法可以互换,

// Transform the normal from object space to world space
// 转换法线 从 模型空间 到 世界空间
o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject); // 内置 UnityObjectToWorldNormal 的实现//等价于
// o.worldNormal = mul((float3x3)unity_ObjectToWorld, v.normal);

此方式只能用在向量上,不能用在顶点上(v.vertex),用在顶点上会得到不一样的结果

Q: 为什么喜欢用 mul(v.normal, (float3x3)unity_WorldToObject); 转换法线 从 模型空间 到 世界空间

等价与 o.worldNormal = mul((float3x3)unity_ObjectToWorld, v.normal);

但是,


名词解释

  • nominator : 分子
  • denominator : 分母 ( 再shader中使用理论公式时常用这个命名变量 )

unity-shader相关相关推荐

  1. 学习Shader Unity Shader 基础

    1.如何充分利用 Unity Shader 来为我们的游戏增光添彩? 材质和 Unity Shader: 在Unity中,我们需要配合使用材质(Material)和 Unity Shader 才能达到 ...

  2. Unity Shader入门精要第3 章 Unity Shader 基础

    Unity系列文章目录 文章目录 Unity系列文章目录 前言 一.Unity Shader 概述 二.使用步骤 1.3.1.2 Unity 中的材质 2.Unity 中的Shader 3.Unity ...

  3. Unity Shader入门精要笔记(五):其他数学相关介绍

    本系列文章由Aimar_Johnny编写,欢迎转载,转载请标明出处,谢谢. http://blog.csdn.net/lzhq1982/article/details/73747162 前两篇介绍了U ...

  4. unity shader入门精要_Unity Shader 入门(一):渲染流水线

    一.参考与说明(需要写在开始东西): 1.1 Unity Shader 入门紧要学习 candycat1992/Unity_Shaders_Book​github.com 1.2 还有一些图形学的历史 ...

  5. 【Unity Shader】(六) ------ 复杂的光照(上)

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

  6. Unity Shader:细分着色器(Tessellation Shader)在Unity顶点着色器中的写法以及各参数变量解释

    图1:在Unity内将sphere细分后 图2:在Unity内将sphere细分后 Unity官网关于细分着色器的资料比较少,只有在Surface Shader中使用的例子.我看了下Surface S ...

  7. 2d shader unity 阴影_【Unity Shader】平面阴影(Planar Shadow)

    来介绍一种适用于移动平台的高性能实时阴影解决方案--平面阴影(Planar Shadow). 由于Unity内置的实时阴影实现方式是屏幕空间阴影贴图(Screen Space Shadow Map)非 ...

  8. Unity Shader知识点(二)写一个基础漫反射Shader

    前言 我们刚学习Shader时,往往会被计算机图形学相关复杂的数学.物理知识所困扰,并在学习过程中丧失兴趣.个人以为,在代码以外,学习Shader应当有本科线性代数课程基础的概念框架,在这一基础上,完 ...

  9. Unity Shader - URP ShadowCast ShadowRecieve - 投影 和 接受阴影

    文章目录 Shadow Caster Using URP Shadow Caster Pass Using Custom Shadow Caster Pass 先来看看 [没有] apply shad ...

  10. Unity Shader - Planar Shadow - 平面阴影

    文章目录 整体运行效果 思路 Shader 问题 Z-Fighting,解决:按法线方向偏移一丢丢 绘制 Alpha 混合重叠,解决:使用 stencil buffer 来规避 为何出现这个问题 解决 ...

最新文章

  1. python 判断debug
  2. 腾讯面试:比特位计数
  3. 中的 隐藏鼠标菜单_如何在鼠标右键菜单中添加自定义菜单?工效率提升一倍...
  4. vs2008创建mysql数据库_求教VS2008 C++连接MySQL数据库
  5. c语言课题设计字符串处理函数报告,C语言输入输出库函数设计实验报告.doc
  6. IE6,7下实现white-space:pre-wrap;
  7. 建立在线社区:Numenta
  8. win10 安装docker
  9. “您都对”、“客户都对”也能申请为商标?
  10. Android View移动的六种方法
  11. 蓝桥杯每日真题之直线
  12. QTP自动化测试—用户登录
  13. centos 7 install VirtualBox
  14. contourlet matlab 源码,contourlet_toolbox matlab 238万源代码下载- www.pudn.com
  15. B 站视频下载器(BV号、dash、音视频分离)
  16. ubuntu系统镜像文件下载
  17. 安卓开发-接收系统广播
  18. 超长干货!最全数据指标分析!
  19. 三星android pie,三星Galaxy系列Android 9.0 Pie
  20. 格雷通路 算法 java,Java算法与数据结构教程

热门文章

  1. php session目录找不到的错误 Error session_start(): open(/var/lib/php/session error
  2. 软件测试中什么最重要_为什么软件测试如此重要
  3. [实用][更新中]Java Apache POI 打印Word文档工具(含文本替换,动态表格功能)
  4. php导出csv_PHP导入导出CSV
  5. java oracle导出csv_Oracle导出CSV文件
  6. 狩鹿助手服务器码,可用KMS服务器
  7. Appendix B5 Review of Memory Hierarchy
  8. 按键控制led灯python程序_按键控制led灯
  9. Everything打开历史记录功能
  10. mysql describe什么意思_MySQL中describe命令的使用方法小结