UnityShader快速上手指南(四)
简介
由于其他项目中断了几天更新,继续~~
这一篇主要是讲光照的(包含漫反射和高光以及多光源的处理)
还是先来看看具体效果(多光源后面单独展示)
有了基本的光照处理之后越来越有立体感了有不有 ╮(╯▽╰)╭
最基本的漫反射
原理
恩~~
这次我们先来解释下原理比较好
比较复杂的原理大家就自行百度吧,我这里来简单的解释一下
光照在物体上面,然后将物体表面的颜色一起反射到摄像机中
也就是光-->物体(颜色)-->摄像机
反射出来的亮度则是由物体表面和光线的夹角确定的(其实是物体表面的法线和光线的夹角)
好了基本概念就降到这里,所以我们需要:
光(方向和强度),物体的颜色,物体表面的法向量
下面开始看代码
代码
Shader "LT/Lesson4_Lighting"
{
Properties
{
_Color ("Diffuse Color", Color) = (1, 1, 1, 1)
}
SubShader
{
Pass {
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
uniform float4 _LightColor0;
uniform float4 _Color;
appdata_base vert ( appdata_base input)
{
float3 normalDirection = normalize( mul(float4(input.normal, 0.0), _World2Object).xyz );
float3 lightDirection;
float attenuation;
if (0.0 == _WorldSpaceLightPos0.w) {
attenuation = 1.0;
lightDirection = normalize(_WorldSpaceLightPos0.xyz);
} else {
float3 vertexToLightSource = (_WorldSpaceLightPos0 - mul( _Object2World , input.vertex)).xyz;
float distance = length(vertexToLightSource);
attenuation = 1.0 / distance;
lightDirection = normalize(vertexToLightSource);
}
float3 diffuseReflection = float3(_LightColor0.xyz ) * _Color.xyz * max(0.0, dot(normalDirection, lightDirection));
input.texcoord = float4(diffuseReflection, 1.0);
input.vertex = mul(UNITY_MATRIX_MVP, input.vertex );
return input;
}
fixed4 frag (appdata_base input) : COLOR
{
return input.texcoord;
}
ENDCG
}
}
}
代码一共就50行,而且很多都是熟悉的,比如用appdata_base 中的不用字段来储存颜色呀,外接漫反射要用的颜色属性啥的就不多做赘述了
然后这个光照颜色其实是在lighting.cginc中 ,但是我们要是用的话,还是要再次声明一下,其实是unity内置的一个外接参数(想想也知道辣,光照的属性肯定是从unity传进来的,shader自己肯定不知道灯光的信息啊)
uniform float4 _LightColor0;
直击核心代码
float3 normalDirection = normalize( mul(float4(input.normal, 0.0), _World2Object).xyz );
// 单位化(有些地方也翻译成归一化)物体的法向量
float3 lightDirection;
// 光照方向
float attenuation;
// 光线衰减量
if (0.0 == _WorldSpaceLightPos0.w) {
// _WorldSpaceLightPos0.w = 0 表示这个光是平行光,
// 不是平行光的话这个值为1,(比如点光源啥的)
attenuation = 1;
// 设置衰减为1(类似于给个默认值)
lightDirection = normalize(_WorldSpaceLightPos0.xyz);
// 设置光照方向
} else {
float3 vertexToLightSource = (_WorldSpaceLightPos0 - mul( _Object2World , input.vertex)).xyz;
float distance = length(vertexToLightSource);
// 计算光源和顶点之间的距离
attenuation = 1.0 / distance;
// 通过距离计算衰减(距离越大,衰减越大)
lightDirection = normalize(vertexToLightSource);
// 设置光照方向
}
float3 diffuseReflection = float3(_LightColor0.xyz ) * _Color.xyz * max(0.0, dot(normalDirection, lightDirection));
// 计算漫反射颜色和亮度(就是前面说的,
// 用物体的颜色和光的颜色混合作为漫反射的颜色,
// 然后法向量和光照的夹角算出光的强度)
恩,然后Pass后面还跟了个Tags { "LightMode" = "ForwardBase" },放到后面一起解释
下面来讲高光
来一点点高光
原理
还是先来原理,跟前面一样,漫反射是360无死角的反射,其实不管摄像机在哪(因为漫反射是360度的嘛,不管摄像机在哪都能反射进去),高光呢就可以理解成针对摄像机的位置,再反射一次光线(加强一次颜色,可以自己设定,一般来说是白色,RBG各种颜色混在一起就是白色了撒,当然也可以使用灯光颜色让他自己去混合)
以上是为了图方便乱吹的啊,具体复杂的原理还是自行百度去吧
下面开始看代码
代码
Shader "LT/Lesson4_Lighting"
{
Properties
{
_Color ("Diffuse Color", Color) = (1, 1, 1, 1)
_SpecColor ("Specular Color", Color) = (1, 1, 1, 1)
_Shininess ("Shininess", Float) = 10
}
SubShader
{
Pass {
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
uniform float4 _LightColor0;
uniform float4 _Color;
uniform float4 _SpecColor;
uniform float _Shininess;
appdata_base vert ( appdata_base input)
{
float3 normalDirection = normalize( mul(float4(input.normal, 0.0), _World2Object).xyz );
float3 viewDirection = normalize( (float4(_WorldSpaceCameraPos, 1.0) - mul( _Object2World, input.vertex)).xyz );
float3 lightDirection = normalize( _WorldSpaceLightPos0.xyz );
float attenuation;
if (0.0 == _WorldSpaceLightPos0.w) {
attenuation = 1.0;
lightDirection = normalize(_WorldSpaceLightPos0.xyz);
} else {
float3 vertexToLightSource = (_WorldSpaceLightPos0 - mul( _Object2World , input.vertex)).xyz;
float distance = length(vertexToLightSource);
attenuation = 1.0 / distance;
lightDirection = normalize(vertexToLightSource);
}
float3 ambientLighting = UNITY_LIGHTMODEL_AMBIENT.xyz * _Color.xyz;
float3 diffuseReflection = float3(_LightColor0.xyz ) * _Color.xyz * max(0.0, dot(normalDirection, lightDirection));
float3 specularReflection;
if (dot(normalDirection, lightDirection) < 0.0) {
specularReflection = float3(0.0, 0.0, 0.0);
} else {
specularReflection = attenuation * _LightColor0.xyz * _SpecColor.xyz *
pow(max(0.0, dot(reflect(-lightDirection, normalDirection),viewDirection)),
_Shininess);
}
input.texcoord = float4(ambientLighting + diffuseReflection + specularReflection, _Color.w);
input.vertex = mul(UNITY_MATRIX_MVP, input.vertex );
return input;
}
fixed4 frag (appdata_base input) : COLOR
{
return input.texcoord;
}
ENDCG
}
}
}
这次代码多了10行左右,大部分代码前面都讲了,我们来直击没讲过的核心代码吧~~
float3 normalDirection = normalize( mul(float4(input.normal, 0.0), _World2Object).xyz );
float3 viewDirection = normalize( (float4(_WorldSpaceCameraPos, 1.0) - mul( _Object2World, input.vertex)).xyz );
// 单位化摄像机的方向
float3 lightDirection = normalize( _WorldSpaceLightPos0.xyz );
float attenuation;
if (0.0 == _WorldSpaceLightPos0.w) {
attenuation = 1.0;
lightDirection = normalize(_WorldSpaceLightPos0.xyz);
} else {
float3 vertexToLightSource = (_WorldSpaceLightPos0 - mul( _Object2World , input.vertex)).xyz;
float distance = length(vertexToLightSource);
attenuation = 1.0 / distance;
lightDirection = normalize(vertexToLightSource);
}
float3 ambientLighting = UNITY_LIGHTMODEL_AMBIENT.xyz * _Color.xyz;
// 计算环境光(其实可以不要,但是高光都有了,没环境光太寒蝉了,可以自行去掉..)
float3 diffuseReflection = float3(_LightColor0.xyz ) * _Color.xyz * max(0.0, dot(normalDirection, lightDirection));
float3 specularReflection;
// 定义高光!
if (dot(normalDirection, lightDirection) < 0.0) {
specularReflection = float3(0.0, 0.0, 0.0);
// 如果光源夹角<0了,说明看不高光了,全0
} else {
specularReflection = attenuation * _LightColor0.xyz * _SpecColor.xyz *
pow(max(0.0, dot(reflect(-lightDirection, normalDirection),viewDirection)),
_Shininess);
// 根据光线颜色,高光颜色,距离衰减,光照角度,法线方向等信息计算高光区域,
// 这些其实都是公式,需要去看看资料吧
}
input.texcoord = float4(ambientLighting + diffuseReflection + specularReflection, _Color.w);
// 将各种颜色加起来赋值给颜色
恩,没什么需要过多解释的了吧,代码里面都注释了
然后下面进入多光源
多光源的简单处理
试过上面的代码的童鞋可以尝试下,再在场景中增加一个灯光(如果加的是点光源,注意点光源的参数范围是否可以照到物体),然后会发现后面增加的光源没有任何效果,这是为神马捏
这里就要引入我们前面跳过的一个概念了
Tags { "LightMode" = "ForwardBase" }
这里的LightMode是与渲染路径配合使用的
我们先来科普下unity支持的各种渲染路径:
看着有四种,其实就三个(好吧,其实是我只在官方文档里面看到三个)
Deferred(延迟渲染,最好的效果),Forward(正向渲染),Vertex(顶点光照,效果最差,默认只有一个光照),还有就是,shader没有写专门支持Deferred的就会自动寻找Forward,没有Forward就寻找Vertex(Vertex是最基本的,如果还没有,就不显示了,一般是不会发生的,因为你不声明LightMode模式默认都是支持Vertex的)
所以对应下来有:
Always: Always rendered; no lighting is applied.
ForwardBase: Used in Forward rendering, ambient, main directional light, vertex/SH lights and lightmaps are applied.
ForwardAdd: Used in Forward rendering; additive per-pixel lights are applied, one pass per light.
Deferred: Used in Deferred Shading; renders g-buffer.
ShadowCaster: Renders object depth into the shadowmap or a depth texture.
PrepassBase: Used in legacy Deferred Lighting, renders normals and specular exponent.
PrepassFinal: Used in legacy Deferred Lighting, renders final color by combining textures, lighting and emission.
Vertex: Used in legacy Vertex Lit rendering when object is not lightmapped; all vertex lights are applied.
VertexLMRGBM: Used in legacy Vertex Lit rendering when object is lightmapped; on platforms where lightmap is RGBM encoded (PC & console).
VertexLM: Used in legacy Vertex Lit rendering when object is lightmapped; on platforms where lightmap is double-LDR encoded (mobile platforms).
英文就不翻译了,还有10分钟要去吃饭了~~看名字大概就看的出来了
这里Always(一直渲染),ShadowCaster(产生阴影),ShadowCollector(接受阴影)是独立于渲染路径存在的(Unity里面设置的)
讲的有点拼远了,这里我们的ForwardBase只会包含一个光源信息(最重要的的光源),然后在ForwardAdd中再将其他光源(不那么重要的光源)渲染出来
所以我们这里需要添加一个Pass,像这样(看后面,多了一个Pass)
Shader "LT/Lesson4_Lighting"
{
Properties
{
_Color ("Diffuse Color", Color) = (1, 1, 1, 1)
_SpecColor ("Specular Color", Color) = (1, 1, 1, 1)
_Shininess ("Shininess", Float) = 10
}
SubShader
{
Pass {
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
uniform float4 _LightColor0;
uniform float4 _Color;
uniform float4 _SpecColor;
uniform float _Shininess;
appdata_base vert ( appdata_base input)
{
float3 normalDirection = normalize( mul(float4(input.normal, 0.0), _World2Object).xyz );
float3 viewDirection = normalize( (float4(_WorldSpaceCameraPos, 1.0) - mul( _Object2World, input.vertex)).xyz );
// 单位化摄像机的方向
float3 lightDirection = normalize( _WorldSpaceLightPos0.xyz );
float attenuation;
if (0.0 == _WorldSpaceLightPos0.w) {
attenuation = 1.0;
lightDirection = normalize(_WorldSpaceLightPos0.xyz);
} else {
float3 vertexToLightSource = (_WorldSpaceLightPos0 - mul( _Object2World , input.vertex)).xyz;
float distance = length(vertexToLightSource);
attenuation = 1.0 / distance;
lightDirection = normalize(vertexToLightSource);
}
float3 ambientLighting = UNITY_LIGHTMODEL_AMBIENT.xyz * _Color.xyz;
// 计算环境光(其实可以不要,但是高光都有了,没环境光太寒蝉了,可以自行去掉..)
float3 diffuseReflection = float3(_LightColor0.xyz ) * _Color.xyz * max(0.0, dot(normalDirection, lightDirection));
float3 specularReflection;
// 定义高光!
if (dot(normalDirection, lightDirection) < 0.0) {
specularReflection = float3(0.0, 0.0, 0.0);
// 如果光源夹角<0了,说明看不高光了,全0
} else {
specularReflection = attenuation * _LightColor0.xyz * _SpecColor.xyz *
pow(max(0.0, dot(reflect(-lightDirection, normalDirection),viewDirection)),
_Shininess);
// 根据光线颜色,高光颜色,距离衰减,光照角度,法线方向等信息计算高光区域,
// 这些其实都是公式,需要去看看资料吧
}
input.texcoord = float4(ambientLighting + diffuseReflection + specularReflection, _Color.w);
// 将各种颜色加起来赋值给颜色
input.vertex = mul(UNITY_MATRIX_MVP, input.vertex );
return input;
}
fixed4 frag (appdata_base input) : COLOR
{
return input.texcoord;
}
ENDCG
}
Pass {
Tags { "LightMode" = "ForwardAdd" }
Blend One One
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
uniform float4 _LightColor0;
uniform float4 _Color;
uniform float4 _SpecColor;
uniform float _Shininess;
appdata_base vert ( appdata_base input)
{
float3 normalDirection = normalize( mul(float4(input.normal, 0.0), _World2Object).xyz );
float3 viewDirection = normalize( (float4(_WorldSpaceCameraPos, 1.0) - mul( _Object2World, input.vertex)).xyz );
float3 lightDirection = normalize( _WorldSpaceLightPos0.xyz );
float attenuation;
if (0.0 == _WorldSpaceLightPos0.w) {
attenuation = 1.0;
lightDirection = normalize(_WorldSpaceLightPos0.xyz);
} else {
float3 vertexToLightSource = (_WorldSpaceLightPos0 - mul( _Object2World , input.vertex)).xyz;
float distance = length(vertexToLightSource);
attenuation = 1.0 / distance;
lightDirection = normalize(vertexToLightSource);
}
float3 ambientLighting = UNITY_LIGHTMODEL_AMBIENT.xyz * _Color.xyz;
float3 diffuseReflection = float3(_LightColor0.xyz ) * _Color.xyz * max(0.0, dot(normalDirection, lightDirection));
float3 specularReflection;
if (dot(normalDirection, lightDirection) < 0.0) {
specularReflection = float3(0.0, 0.0, 0.0);
} else {
specularReflection = attenuation * _LightColor0.xyz * _SpecColor.xyz *
pow(max(0.0, dot(reflect(-lightDirection, normalDirection),viewDirection)),
_Shininess);
}
input.texcoord = float4(ambientLighting + diffuseReflection + specularReflection, _Color.w);
input.vertex = mul(UNITY_MATRIX_MVP, input.vertex );
return input;
}
fixed4 frag (appdata_base input) : COLOR
{
return input.texcoord;
}
ENDCG
}
}
}
这里我们还启用了Blend One One
因为其他光源反射的颜色需要完全与原来的光照信息混合,所以用1:1混合
来看看效果:
这里高光的效果很明显,可以看到两个高光点( 视图中也可以看到有两个光源)
然后使用Tags { "LightMode" = "ForwardAdd" }的好处还可以适应质量调节(比如Android打包的时候选成Vertex,高于Vertex的Pass就不会执行啥的)
总结
还有两分钟吃饭
光照啥的代码其实很简单,最主要是理清楚顺序,比如先渲染啥后渲染啥,也就是Pass的顺序,不光是光照,后续很多效果都需要多个Pass进行叠加啥的
不懂得,欢迎联系QQ:821580467
吃饭去咯~~~~
转载请注明出处 http://blog.csdn.net/ghostwolfliu/article/details/51596911
转载请注明出处 http://www.cnblogs.com/coldcode/p/5564687.html
UnityShader快速上手指南(四)的更多相关文章
- UnityShader快速上手指南(三)
简介 这一篇还是一些基本的shader操作:裁剪.透明和法向量的应用 (纠结了很久写不写这些,因为代码很简单,主要是些概念上的东西) 先来看下大概的效果图:(从左到右依次是裁剪,透明,加了法向量的透明 ...
- UnityShader快速上手指南(二)
简介 前一篇介绍了如果编写最基本的shader,接下来本文将会简单的深入一下,我们先来看下效果吧 呃,gif效果不好,实际效果是很平滑的动态过渡 实现思路 1.首先我们要实现一个彩色方块 2.让色彩动 ...
- UnityShader快速上手指南(一)
简介 引言 其实网上有很多shader教程,但是大概看了下,也不知是网上各位大神已经脱离了代码层面的高度还是啥原因.貌似没有找到从代码方面作为入门讲解的,导致了shader对于苦逼程序员入门有一定要求 ...
- Rancher 快速上手指南操作(1)
Rancher 快速上手指南操作(1)该指南知道用户如何快速的部署Rancher Server 管理容器.前提是假设你的机器已经安装好docker了.1 确认 docker 的版本,下面是 ubunt ...
- [转]Rancher 快速上手指南操作(1)
本文转自:http://www.cppblog.com/zhiyewang/archive/2016/03/17/213053.aspx Rancher 快速上手指南操作(1)该指南知道用户如何快速的 ...
- Markdown快速上手指南
Markdown快速上手指南 1.Markdown介绍 markdown可以实现快速html文档编辑,格式优没,并且不需要使用html元素. markdown采用普通文本的形式,例如读书笔记等易于使用 ...
- Mac快速上手指南
上周刚入手了2017版MacBookPro,预装macOS High Sierra.第一次接触Mac系统,经过一周的使用,简单总结下与Windows相比最常用的功能,快速上手. 1.Mac键盘实现Ho ...
- Github Action 快速上手指南
前言 各位读者,新年快乐,我是过了年匆忙赶回上海努力搬砖的蛮三刀. Github之前更新了一个Action功能(应该是很久以前了),可以实现很多自动化操作.用来替代用户自己设置的自动化脚本(比如:钩子 ...
- IDEA 快速上手指南(全配置)(Day_23)
Idea快速入门指南 1.安装 1.1.安装 我们使用的是2017.3.4版本: 双击打开, 选择一个目录,最好不要中文和空格: 然后选择桌面快捷方式,请选择64位: 然后选择安装: 开始安装: 然后 ...
随机推荐
- Scala 深入浅出实战经典 第42讲:scala 泛型类,泛型函数,泛型在spark中的广泛应用
王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-64讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...
- 互联网的寒冬来了,BAT都不社招了
一 总理上次来到创业街,是四个月,要不就是五个月前了. 之后,全国创业形势一路走红,锣鼓喧天鞭炮齐鸣.大众创业万众创新,颇有大炼钢铁亩产万斤之势,尤其在媒体上. 再之后,2015 进入下半年,风投圈的 ...
- Linux 统计某个字符串出现的次数
要统计一个字符串出现的次数,这里现提供自己常用两种方法: 1. 使用vim统计 用vim打开目标文件,在命令模式下,输入 :%s/objStr//gn 即可 2. 使用grep: grep -o ob ...
- Ant自动编译打包&发布 android项目
Eclipse用起来虽然方便,但是编译打包android项目还是比较慢,尤其将应用打包发布到各个渠道时,用Eclipse手动打包各种渠道包就有点不切实际了,这时候我们用到Ant帮我们自动编译打包了. ...
- sqlite 的比较等运算是根据不同的值而不同的,并不是根据的字段类型,因为 sqlite 是弱类型字段
sqlite 的比较等运算是根据不同的值而不同的,并不是根据的字段类型,因为 sqlite 是弱类型字段 --------------------------------------------- ...
- 桌面oracle 11g导入多年库的dump备忘
接到客户6G的dump文件.先导入桌面orcale ,imp提示出错,执行impdp后如下 连接到: Oracle Database 11g Express Edition Release 11.2. ...
- 全渠道后端 : RFID在仓储物流中的运用
核心提示 沃尔玛的业务之所以能够迅速增长,并且成为现在非常著名的公司之一,是因为沃尔玛在节省成本以及在物流配送系统与供应链管理方面取得了巨大的成就. 最后为一个业务展望,从B端工厂提货 到C端交付的系 ...
- MariaDB Galera Cluster 10.1 只支持 LINUX ?!
MariaDB Galera Cluster (MariaDB 10.1) 当前只支持:LINUX ! 参考: https://mariadb.com/kb/en/mariadb/getting-s ...
- Embedding Python in C
http://codextechnicanum.blogspot.com/2013/12/embedding-python-in-c-converting-c.html //Make some vec ...
- Zynq学习笔记(1)
做硬件的第一个实例,一般当然是LED点灯啦~ 硬件:ZedBoard 软件:ISE 14.7 1.新建工程 2.选择平台 3.新建完成后,输入如下代码: `timescale 1ns / 1ps // ...