转自http://www.itnose.net/detail/6117338.html

前文中完成最简单的漫反射shader只是单个光源下的漫反射,而往往场景中不仅仅只有一个光源,那么多个光源的情况下我们的物体表面的漫反射强度如何叠加在一起呢?前文打的tag "LightMode"="ForwardBase"又是什么意思呢?

Unity内置的DiffuseShader,也就是我们创建一个Material出来时默认的Shader也是多光源的,所以这篇文章完成的shader与默认的diffuse shader基本效果一致。

首先引入几个概念

渲染路径 Rendering Path

Unity在处理多光源的情况时为我们提供了三种模式:修改的地方在  Edit--Project Settings--Player--Other Settings--Rendering Path
1.顶点光 Vertex Lit
2.方向性 Forward (默认)
3.延迟照明 Deferred Lighting 
我们的shader也使用默认的Forward

像素光 Pixel Light

Unity中将平行光称作为像素光,第一个像素光是基础平行光,以LightMode=ForwardBase标签修饰,每多一个像素光都以LightMode=ForwardAdd标签修饰。
并不是所有的光源在运行时都会反射到物体上,而是根据Project的Quality中设置的像素光数量来渲染的。
 
默认像素光的数量应该是2,我们有更多的平行光照在物体上,就需要在Edit > Project Settings > Quality中去调节像素光的数量Pixel Light Count,如下图:
 
 
 
当场景中的实际像素光数量超过这个设定值的时候,unity只会渲染最重要的光。至于如何将光源的渲染模式改为重要和不重要我们后面将继续讨论。
 
 

关于像素光的叠加原理

前面的系列6中已经讲过片段着色器是要将mesh组件传递的信息最终计算为颜色(或者深度)存储在帧缓存(Frame Buffer)中。
每个Pass之间输出的颜色通过一定的公式进行混合。
 
在这里我们简单实用一比一的模式进行颜色混合,即混合指令为:
 
Blend One One
 
第二个Pass的代码同样的也直接复制第一个Pass即可,相应的将Tags标签中的LightMode=ForwardBase修改为LightMode=ForwardAdd。
 
代码
Shader "Custom/Multi-Light Diffuse" {
Properties {
//材料颜色默认为黑色,可在inspector中调节
_Color ("Material Color", Color) = (1,1,1,1)
}
SubShader { Pass{ //第一个像素光所在的反射通道标记为ForwardBase
Tags {"LightMode" = "ForwardBase"} CGPROGRAM
// Upgrade NOTE: excluded shader from OpenGL ES 2.0 because it does not contain a surface program or both vertex and fragment programs.
//#pragma exclude_renderers gles
//定义顶点着色器与片段着色器入口
#pragma vertex vert
#pragma fragment frag
//获取property中定义的材料颜色
uniform float4 _Color; // 光源的位置或者方向
//uniform float4 _WorldSpaceLightPos0; // 光源的颜色 (from "Lighting.cginc")
uniform float4 _LightColor0; //定义顶点着色器的输入参数结构体
//我们只需要每个顶点的位置与对应的法向量
struct vertexInput {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
//定义顶点着色的输出结构体/片段着色的输入结构体
//已经计算好的颜色
struct vertexOutput {
float4 pos : SV_POSITION;
float4 col : COLOR;
}; //顶点着色器
vertexOutput vert (vertexInput input) {
vertexOutput output;
//对象坐标系到世界坐标系的变换矩阵
//_Object2World与_World2Object均为unity提供的内置uniform参数
float4x4 modelMatrix = _Object2World;
//世界坐标系到对象坐标系的变换矩阵
float4x4 modelMatrixInverse = _World2Object; //计算对象坐标系中的顶点法向量的单位向量
//将mesh传递过来的顶点法向量与模型-->对象坐标系矩阵相乘得到对象坐标系中的法向量
//然后单位化
float3 normalDirection = normalize(float3(mul(float4(input.normal, 0.0), modelMatrixInverse))); //计算入射向量的单位向量
float3 lightDirection = normalize(float3(_WorldSpaceLightPos0)); //计算反射后的颜色
//先将光源颜色与材料颜色向量相乘
//再乘以上文提到的max(0,cos∠(N,L))
float3 diffuseReflection=float3(_LightColor0) * float3(_Color)* max(0.0, dot(normalDirection, lightDirection)); //上面计算的是RGB颜色,差个A,补充一维就可以传递给片段着色器了
output.col=float4(diffuseReflection,1); //国际惯例,顶点变化三步曲,这个例子中可写可不写
output.pos = mul(UNITY_MATRIX_MVP, input.vertex); return output;
} //片段着色器,老规矩,把顶点着色器的输出参数作为片段着色器的输入参数
float4 frag(vertexOutput input): COLOR
{
return input.col; } ENDCG
} Pass{ //其余的像素光所在的反射通道标记为ForwardAdd
Tags {"LightMode" = "ForwardAdd"} //此通道的片段着色器输出颜色时帧缓存已经有了颜色,所以需要混合
//混合模式为1比1 Blend One One
CGPROGRAM
// Upgrade NOTE: excluded shader from OpenGL ES 2.0 because it does not contain a surface program or both vertex and fragment programs.
//#pragma exclude_renderers gles
//定义顶点着色器与片段着色器入口
#pragma vertex vert
#pragma fragment frag
//获取property中定义的材料颜色
uniform float4 _Color; // 光源的位置或者方向
//uniform float4 _WorldSpaceLightPos0; // 光源的颜色 (from "Lighting.cginc")
uniform float4 _LightColor0; //定义顶点着色器的输入参数结构体
//我们只需要每个顶点的位置与对应的法向量
struct vertexInput {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
//定义顶点着色的输出结构体/片段着色的输入结构体
//已经计算好的颜色
struct vertexOutput {
float4 pos : SV_POSITION;
float4 col : COLOR;
}; //顶点着色器
vertexOutput vert (vertexInput input) {
vertexOutput output;
//对象坐标系到世界坐标系的变换矩阵
//_Object2World与_World2Object均为unity提供的内置uniform参数
float4x4 modelMatrix = _Object2World;
//世界坐标系到对象坐标系的变换矩阵
float4x4 modelMatrixInverse = _World2Object; //计算对象坐标系中的顶点法向量的单位向量
//将mesh传递过来的顶点法向量与模型-->对象坐标系矩阵相乘得到对象坐标系中的法向量
//然后单位化
float3 normalDirection = normalize(float3(mul(float4(input.normal, 0.0), modelMatrixInverse))); //计算入射向量的单位向量
float3 lightDirection = normalize(float3(_WorldSpaceLightPos0)); //计算反射后的颜色
//先将光源颜色与材料颜色向量相乘
//再乘以上文提到的max(0,cos∠(N,L))
float3 diffuseReflection=float3(_LightColor0) * float3(_Color)* max(0.0, dot(normalDirection, lightDirection)); //上面计算的是RGB颜色,差个A,补充一维就可以传递给片段着色器了
output.col=float4(diffuseReflection,1); //国际惯例,顶点变化三步曲,这个例子中可写可不写
output.pos = mul(UNITY_MATRIX_MVP, input.vertex); return output;
} //片段着色器,老规矩,把顶点着色器的输出参数作为片段着色器的输入参数
float4 frag(vertexOutput input): COLOR
{
return input.col; } ENDCG
}
}
}

  

这样我们就能在添加第二个光源之后看到两个光源的效果了,在这里为了对比效果我们保留上一个例子中的单光源漫反射shader,然后分别创建两个球体,在场景中设置2个光源,看一下对比的效果:
 
 
 
 
可以看到同一个场景中 两个平行光照在两个球体上,一个只体现了主光源的漫反射(ForwardBase),而另一个将两个平行光的漫反射混合了起来。
 
如果场景中再添加以个平行光,那么我们再添加一个Pass 标记为ForwardAdd,并且在Quality中将Pixcel Light Count 调节为3即可。

解读Unity中的CG编写Shader系列八(多光源漫反射)的更多相关文章

  1. [转]解读Unity中的CG编写Shader系列8——多光源漫反射

    前文中完成最简单的漫反射shader只是单个光源下的漫反射,而往往场景中不仅仅只有一个光源,那么多个光源的情况下我们的物体表面的漫反射强度如何叠加在一起呢?前文打的tag "LightMod ...

  2. 解读Unity中的CG编写Shader系列八(镜面反射)

    转自http://www.itnose.net/detail/6117378.html 讨论完漫反射之后,接下来肯定就是镜面反射了 在开始镜面反射shader的coding之前,要扩充一下前面提到的知 ...

  3. [转]解读Unity中的CG编写Shader系列9——镜面反射

    讨论完漫反射之后,接下来肯定就是镜面反射了在开始镜面反射shader的coding之前,要扩充一下前面提到的知识,加深理解镜面反射与漫反射的区别.注:这篇文章实现的镜面反射是逐顶点着色(per-ver ...

  4. [转]解读Unity中的CG编写Shader系列7——漫反射

    如果前面几个系列文章的内容过于冗长缺乏趣味着实见谅,由于时间原因前面的混合部分还没有写完,等以后再补充,现在开始关于反射的内容了.折射与反射在物理世界中,光的反射与折射往往是同时存在的,光源由真空或者 ...

  5. 解读Unity中的CG编写Shader系列七(不透明度与混合)

    转自http://www.itnose.net/detail/6098539.html 1.不透明度 当我们要将两个半透的纹理贴图到一个材质球上的时候就遇到混合的问题,由于前面的知识我们已经知道了片段 ...

  6. 解读Unity中的CG编写Shader系列三

    转自http://www.itnose.net/detail/6096068.html 在上一个例子中,我们得到了由mesh组件传递的信息经过数学转换至合适的颜色区间以颜色的形式着色到物体上.这篇文章 ...

  7. [转]解读Unity中的CG编写Shader系列6——不透明度与混合

    1.不透明度当我们要将两个半透的纹理贴图到一个材质球上的时候就遇到混合的问题,由于前面的知识我们已经知道了片段着色器以及后面的环节的主要工作是输出颜色与深度到帧缓存中,所以两个纹理在每个像素上的颜色到 ...

  8. [转]解读Unity中的CG编写Shader系列3——表面剔除与剪裁模式

    在上一个例子中,我们得到了由mesh组件传递的信息经过数学转换至合适的颜色区间以颜色的形式着色到物体上.这篇文章将要在此基础上研究片段的擦除(discarding fragments)和前面剪裁.后面 ...

  9. 解读Unity中的CG编写Shader系列3——表面剔除与剪裁模式

    在上一个样例中,我们得到了由mesh组件传递的信息经过数学转换至合适的颜色区间以颜色的形式着色到物体上. 这篇文章将要在此基础上研究片段的擦除(discarding fragments)和前面剪裁.后 ...

随机推荐

  1. Entity Framework数据库初始化四种策略

    策略一:数据库不存在时重新创建数据库 程序代码 Database.SetInitializer<testContext>(new CreateDatabaseIfNotExists< ...

  2. ionic导航之后返回功能的说明

    当我导航view之后,再使用$location.path("/path/origin")方法重新定位到初始页面,在深入进入其他的view之后使用这个方法就遇到了问题. 假设这个设置 ...

  3. Entity Framework浅析

    1.Entity Framework简介 http://www.cnblogs.com/aehyok/p/3315991.html 2.Entity Framework DBFirst尝试http:/ ...

  4. html5开发制作,漂亮html5模板欣赏,H5网站建设

    html5是什么? HTML5 是下一代的 HTML(超文本标记语言,网页的组成部分),HTML5是web开发世界的一次重大的改变,能适配pc.手机等各终端,跨平台性能极强,移动互联网是未来的趋势,h ...

  5. SQL Server 查询、搜索命令、语句

    --查询所有表 SELECT NAME,* FROM SYSOBJECTS WHERE XTYPE='U' order by SYSOBJECTS.name --查询所有存储过程 select * f ...

  6. 关于ext3,ext4,xfs和btrfs文件系统性能对比

    关于ext3,ext4,xfs和btrfs文件系统性能对比 应为原文:http://www.ilsistemista.net/index.php/linux-a-unix/6-linux-filesy ...

  7. canvas游戏小试:画一个按方向键移动的圆点

    canvas游戏小试:画一个按方向键移动的圆点   自己对canvas,但又有一颗做游戏的心TT.然后记录一下对canvas的学习吧,用一个按方向键控制的小圆点来做练习.(编程时用了一些es6的语法) ...

  8. union联合体

    今天笔试的一道题,好久没用union了,竟然忘光光了. 关于其大小的计算,分两步:先算对齐大小(成员中字节最大的那个),再算分配空间: 不仅是对齐大小的整数倍,还要满足实际大小不能小于最大成员大小. ...

  9. 《lucene原理与代码分析》笔记

    1.全文索引相对于顺序扫描的优势:一次索引,多次使用 2.创建索引的步骤:(1)要索引的原文档 (2)将原文档传给分词组件(Tokenizer)分词组件会做如下事情:(此过程称为Tokenize)a. ...

  10. Kali Linux渗透基础知识整理(一):信息搜集

    写在前面的废话:最近要给一些新人做培训,整理些东西,算不上什么太高端的内容,只是简单的整理下了,我觉得对于小白的话也还算是干货.在乌云水了几年,算不上什么大神水平,最近生活费紧张,现在打算在FreeB ...