【Unity Shaders】Lighting Models —— 灯型号Lit Sphere
考《Unity Shaders and Effects Cookbook》一书(感谢原书作者),同一时候会加上一点个人理解或拓展。
这里是本书全部的插图。这里是本书所需的代码和资源(当然你也能够从官网下载)。
========================================== 切割线 ==========================================
写在前面
实际上,我们能够使用一张2D贴图来完整地烘焙我们的光照。你能够得到Zbrush这个软件实现的同样效果。假设你对Zbrush的MatCaps(Material Captures)非常熟悉,那么恭喜你。被照亮的球体是同样的实现原理。我们能够创建一个贴图,然后全然照搬各种烘焙类型,像漫反射,镜面反射,反射以及边缘光照等效果。然后再使用它来创建我们的Shader。
这样的方法唯一的缺点就是。由于我们将光照全然烘焙到了贴图上,因此无法改变光照,除非你依据你的环境替换还有一张贴图。就像我们在反射一章中的简单的Cubemap反射一样。也就是说。这个Shader不会依据你环境中的光照做出不论什么变化,也不会再你移动你的视角时产生不论什么改变。下图展示了一个Lit Sphere贴图。它通常被称为一个Sphere Map:
用更易懂的话来说,当给定一张贴图后。使用它渲染得到的画面将和贴图看起来一样,就像把贴图投影到了模型上一样。
准备工作
这个是一个非常棒的免费软件能够帮你创建这些Lit Sphere maps。作者建议观看Vimeo上的视频(须要翻墙)来帮助你熟悉MaCrea的界面和工作流程。
- 创建一个新的场景以及一些对象,一个平面以及一个平行光。
- 创建一个新的Shader 和材质,然后将你的Shader赋给新的材质。
实现和解释
- 和之前一样。我们须要一些properties传递给Surface Shader。以便我们能够让这个Shader的用户更改贴图以及一些变量。
因此,我们首先加入以下的代码到Properties块:
Properties {
_MainTint ("Diffuse Tint", Color) = (1,1,1,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
_NormalMap ("Normal Map", 2D) = "bump" {}
} - 由于我们的Shader仅使用贴图来照亮我们的模型,因此我们不须要内置的Lambert光照函数。而须要声明我们自己的Unlit光照函数。
我们还须要写一个顶点函数:
CGPROGRAM
#pragma surface surf Unlit vertex:vert解释:鉴于有些童鞋忘了或者没有看过之前的内容,这里说明一下上面这句声明的意思。
它表明我们将使用名为surf的Surface Shader function。以及名为Unlit的自己定义光照函数,还有一个名为vert的顶点函数。本书第一次提到这个知识点是在这一节。
- 和前面一样,我们须要在块中声明之前的properties,以便我们能够利用Inspector中的各个用户给定的数据。
float4 _MainTint;
sampler2D _MainTex;
sampler2D _NormalMap; - 接下来。我们创建一个新的光照函数。它相应上面提到的名为Unlit的光照模型。这样做是由于我们不想使用场景中的灯光影响我们的Shader。我们只想要得到对象的阴影,其它的交给贴图来照亮整个对象。
因此,我们须要加入以下的光照函数(这里跟原书有一点不一样,我依据官方文档改动了返回类型):
inline half4 LightingUnlit (SurfaceOutput s, fixed3 lightDir, fixed atten)
{
half4 c = half4(1,1,1,1);
c.rgb = s.Albedo;
c.a = s.Alpha;
return c;
}解释:同样,关于这里的内容能够參见这一节。
- 如今,我们须要将一些额外的属性加入到Input结构体中。以便我们能够将这些信息从vert()函数传递给surf()函数:
struct Input {
float2 uv_MainTex;
float2 uv_NormalMap;
float3 tan1;
float3 tan2;
};解释:你能够把Input结构体当成是顶点函数和Surface shading函数之间的纽带,它们之间的沟通都是靠Input结构体进行传递的。
- 为了正确的在Sphere map中查找。我们须要将切线旋转矩阵(the rotated tangent vector)和当前模型的逆转置模型视图矩阵(the inverse transpose model view matrix)相乘。这将会给你合适的想来应用到Sphere map贴图中。假设你还是不明确也不要紧。在后面我们会进一步说明。
void vert (inout appdata_full v, out Input o)
{
UNITY_INITIALIZE_OUTPUT(Input,o); TANGENT_SPACE_ROTATION;
o.tan1 = mul(rotation, UNITY_MATRIX_IT_MV[0].xyz);
o.tan2 = mul(rotation, UNITY_MATRIX_IT_MV[1].xyz);
}解释:vert()函数是这个光照模型横纵真正起作用的地方。我们使用模型的切线旋转矩阵和它的逆转置模型视图进行相乘。它的结果将在以下被用于查找Sphere map中相应的位置。那么,逆转置模型视图矩阵是从哪里产生的呢?事实上这是Unity提供的还有一个内置參数。因此我们不须要再自己计算它了。
实际上Unity提供了绝大多数在标准的CGFX shaders中常见的转换矩阵。这是我们使用Surface Shader的一个优点之中的一个,我们不须要自己计算它们了。而只须要调用内置的參数。
原书并没有给出这一步的解释,我这里补充一下。这个shader的精髓就在于它是像投影一样。全然平铺在Sphere上的。我们能够想象它的本质。就是在Eye Space中,依据顶点法线在X和Y轴上的投影作为UV坐标,对纹理进行採样。
而作者在这里选择把计算统一到Tangent Space中。因此。这里的tan1和tan2就分别相应了在Tangent Space中。Eye Space的X轴和Y轴方向。
这样的写法是推导了一步后的结果,原始的计算代码例如以下:
o.tan1 = mul(rotation, mul(float3(1.0f, 0.0, 0.0f), (float3x3)UNITY_MATRIX_IT_MV));
o.tan2 = mul(rotation, mul(float3(0.0f, 1.0, 0.0f), (float3x3)UNITY_MATRIX_IT_MV));rotation负责把方向从Object Space转换到Tangent Space。因此我们首先须要把Eye Space的X和Y轴转换到Object Space中, mul(float3(1.0f, 0.0, 0.0f), (float3x3)UNITY_MATRIX_IT_MV)负责这个转换步骤。这是由于。假设我们想要把X轴从Eye Space转换到Tangent Space,就须要从Eye Space到Tangent Space的变换矩阵。即我们希望有UNITY_MATRIX_MV的逆矩阵。
然而,Unity没有直接提供这个矩阵么认识提供了UNITY_MATRIX_IT_MV,即UNITY_MATRIX_MV的逆转置矩阵。但我们仍然能够通过mul中交换变换矩阵和向量的位置,来得到同样的效果。
然后在通过外层的mul把方向从Object Space转换到Tangent Space就可以。还是不明确的能够看一下这篇文章。
- 最后,我们须要实现我们的surf()函数。进行一些计算来产生相应的Sphere map中的查找值,并把它们赋给我们的结构体。
同样,这个部分将在后面进行说明:
void surf (Input IN, inout SurfaceOutput o)
{
float3 normals = UnpackNormal(tex2D(_NormalMap, IN.uv_NormalMap));
o.Normal = normals; float2 litSphereUV;
litSphereUV.x = dot(IN.tan1, o.Normal);
litSphereUV.y = dot(IN.tan2, o.Normal); half4 c = tex2D (_MainTex, litSphereUV*0.5+0.5);
o.Albedo = c.rgb * _MainTint;
o.Alpha = c.a;
}
完整代码
Shader "Custom/LitSphere" {
Properties {
_MainTint ("Diffuse Tint", Color) = (1,1,1,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
_NormalMap ("Normal Map", 2D) = "bump" {}
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Unlit vertex:vert
float4 _MainTint;
sampler2D _MainTex;
sampler2D _NormalMap;
inline half4 LightingUnlit (SurfaceOutput s, fixed3 lightDir, fixed atten)
{
half4 c = half4(1,1,1,1);
c.rgb = s.Albedo;
c.a = s.Alpha;
return c;
}
struct Input {
float2 uv_MainTex;
float2 uv_NormalMap;
float3 tan1;
float3 tan2;
};
void vert (inout appdata_full v, out Input o)
{
UNITY_INITIALIZE_OUTPUT(Input,o);
TANGENT_SPACE_ROTATION;
o.tan1 = mul(rotation, UNITY_MATRIX_IT_MV[0].xyz);
o.tan2 = mul(rotation, UNITY_MATRIX_IT_MV[1].xyz);
}
void surf (Input IN, inout SurfaceOutput o)
{
float3 normals = UnpackNormal(tex2D(_NormalMap, IN.uv_NormalMap));
o.Normal = normals;
float2 litSphereUV;
litSphereUV.x = dot(IN.tan1, o.Normal);
litSphereUV.y = dot(IN.tan2, o.Normal);
half4 c = tex2D (_MainTex, litSphereUV*0.5+0.5);
o.Albedo = c.rgb * _MainTint;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
写在最后
【Unity Shaders】Lighting Models —— 灯型号Lit Sphere的更多相关文章
- 【Unity Shaders】Lighting Models —— 光照模型之Lit Sphere
本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...
- 【Unity Shaders】Lighting Models 介绍
本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...
- 【Unity Shaders】Lighting Models —— 衣服着色器
本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...
- 【Unity Shaders】Diffuse Shading——创建一个自定义的diffuse lighting model(漫反射光照模型)
本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...
- 【Unity Shaders】学习笔记——SurfaceShader(三)BasicDiffuse和HalfLambert
[Unity Shaders]学习笔记——SurfaceShader(三)BasicDiffuse和HalfLambert 转载请注明出处:http://www.cnblogs.com/-867259 ...
- 【Unity Shaders】使用CgInclude让你的Shader模块化——创建CgInclude文件存储光照模型
本系列主要參考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同一时候会加上一点个人理解或拓展. 这里是本书全部的插图. 这里是本书所需的代码 ...
- 【Unity Shaders】法线纹理(Normal Mapping)的实现细节
写在前面 写这篇的目的是为了总结我长期以来的混乱.虽然题目是"法线纹理的实现细节",但其实我想讲的是如何在shader中编程正确使用法线进行光照计算.这里面最让人头大的就是各种矩阵 ...
- 【Unity Shaders】学习笔记——SurfaceShader(十一)光照模型
[Unity Shaders]学习笔记——SurfaceShader(十一)光照模型 转载请注明出处:http://www.cnblogs.com/-867259206/p/5664792.html ...
- 【Unity Shaders】学习笔记——SurfaceShader(十)镜面反射
[Unity Shaders]学习笔记——SurfaceShader(十)镜面反射 如果你想从零开始学习Unity Shader,那么你可以看看本系列的文章入门,你只需要稍微有点编程的概念就可以. 水 ...
随机推荐
- swiper轮播控件配置项
var mySwiper = new Swiper ('.swiper-container', { direction: 'horizontal', loop: true, auto ...
- axios封装http请求
import axios from 'axios' const HTTP_TIMEOUT = 15000; export function httpPost(url, params = {},head ...
- [JSOI2009]计数问题
一个n*m的方格,初始时每个格子有一个整数权值.接下来每次有2种操作: 改变一个格子的权值: 求一个子矩阵中某种特定权值出现的个数. 输入输出格式 输入格式: 第一行有两个数N,M. 接下来N行,每行 ...
- C# 报表
报表技术 1.OWC控件的使用 OWC是office web Components 是组件对象模型(COM)控件的集合,可用于将电子表格,图表和数据库发布到网站上. 在Office2003以后的版本中 ...
- 无状态会话bean(1)---定义
无状态会话bean用于完毕在单个方法的生命周期内的操作.无状态bean能够实现很多业务操作,可是每一个方法都不能假定不论什么其它的方法会在它之前调用.后半句的意思是如今的你可能不是刚才的你.明天的你可 ...
- JAVA开发类似冒险岛的游戏Part1
JAVA开发类似冒险岛的游戏Part1 一.总结 二.JAVA开发类似冒险岛的游戏Part1 初学嘛) ,不过总的来说这个程序还是很有意思的.这里我重新再整理了一下,希望能帮助到其他想要开发类似程序的 ...
- python基础-合并列表
1.append() 向列表尾部追加一个新元素,列表只占一个索引位,在原有列表上增加 2.extend() 向列表尾部追加一个列表,将列表中的每个元素都追加进来,在原有列表上增加 3.+ 直接用+ ...
- nokia 5220 XpressMusic 自己刷机
看了半天各种论坛,是在不知道从哪里下手,所以自己写一篇自己刷机的新的.凤凰那个软件好像已经挂了,每次打开就是service is not authorized. 所以还是使用nokia自己的官方下载平 ...
- __block typeof的说明
1. block不是Object对象,所以对retain无效,要想保留block生命周期,最好通过copy来实现,当然copy后,要记得release. 2.一般被block的应用的对象,retain ...
- 利用朴素贝叶斯(Navie Bayes)进行垃圾邮件分类
贝叶斯公式描写叙述的是一组条件概率之间相互转化的关系. 在机器学习中.贝叶斯公式能够应用在分类问题上. 这篇文章是基于自己的学习所整理.并利用一个垃圾邮件分类的样例来加深对于理论的理解. 这里我们来解 ...