UnityShader快速上手指南(四)

转载请注明出处 http://blog.csdn.net/ghostwolfliu/article/details/51596911

转载请注明出处 http://www.cnblogs.com/coldcode/p/5564687.html

简介

由于其他项目中断了几天更新,继续~~
这一篇主要是讲光照的(包含漫反射和高光以及多光源的处理)
还是先来看看具体效果(多光源后面单独展示)

有了基本的光照处理之后越来越有立体感了有不有 ╮(╯▽╰)╭

最基本的漫反射

原理

恩~~

这次我们先来解释下原理比较好

比较复杂的原理大家就自行百度吧,我这里来简单的解释一下

光照在物体上面,然后将物体表面的颜色一起反射到摄像机中

也就是光–>物体(颜色)–>摄像机

反射出来的亮度则是由物体表面和光线的夹角确定的(其实是物体表面的法线和光线的夹角)

好了基本概念就降到这里,所以我们需要:

光(方向和强度),物体的颜色,物体表面的法向量

下面开始看代码

代码

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

吃饭去咯~~~~

时间: 2024-08-04 20:35:20

UnityShader快速上手指南(四)的相关文章

UnityShader快速上手指南(三)

简介 这一篇还是一些基本的shader操作:裁剪.透明和法向量的应用 (纠结了很久写不写这些,因为代码很简单,主要是些概念上的东西) 先来看下大概的效果图:(从左到右依次是裁剪,透明,加了法向量的透明) 裁剪 代码 Shader "LT/Lesson3_Cull" { Properties { _Color ("Color", Color) = (1, 1, 1, 1) } SubShader { Pass { Cull Off CGPROGRAM #pragma

UnityShader快速上手指南(二)

简介 前一篇介绍了如果编写最基本的shader,接下来本文将会简单的深入一下,我们先来看下效果吧 呃,gif效果不好,实际效果是很平滑的动态过渡 实现思路 1.首先我们要实现一个彩色方块 2.让色彩动起来 over 实现一个RGB CUBE 先看代码吧: Shader "LT/Lesson2" { Properties { _OffsetX ("Offset X", Range (-1.5, 1.5) ) = 0 _OffsetY ("Offset Y&q

DPDK快速上手指南(18.02)

DPDK快速上手(linux) 本文档主要来自linux_gsg-18.02.pdf的翻译,翻译肯定有不妥之处,请批评指正,我会随后修改,不胜感激. 1. 介绍 本文档包含有关DPDK(Data Plane Development Kit的缩写)软件的安装和配置的说明,目的就是让用户快速用起来.本文档描述怎样在linux应用环境下编译和运行一个DPDK应用程序,而不过多深入细节. 1.1文档路线图 以下是针对所有DPDK文档建议的阅读顺序: 发布说明(Release Notes):提供具体的发布

Rancher 快速上手指南操作(1)

Rancher 快速上手指南操作(1)该指南知道用户如何快速的部署Rancher Server 管理容器.前提是假设你的机器已经安装好docker了.1 确认 docker 的版本,下面是 ubuntu 的输出 [#63#[email protected] ~]$sudo docker version [sudo] password for cloudsoar: Client: Version:      1.9.1 API version:  1.21 Go version:   go1.4.

Mac快速上手指南

上周刚入手了2017版MacBookPro,预装macOS High Sierra.第一次接触Mac系统,经过一周的使用,简单总结下与Windows相比最常用的功能,快速上手. 1.Mac键盘实现Home.End.Page UP.Page DOWN这几个键 macbookpro键盘没有Home.End.Page UP.Page DOWN这几个键,不过,平时浏览网页.文档还需要的: Home键=Fn+左方向 End键=Fn+右方向 PageUP=Fn+上方向 PageDOWN=Fn+下方向 2.D

Android快速上手指南(WIP)

JNI是java调用C/C++的一种封装技术,由JVM负责处理真实的JNI call. Java官方的文档 http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html Android平台的 http://developer.android.com/training/articles/perf-jni.html 快速上手的代码范例: https://android.googlesource.com/pl

webpack构建工具快速上手指南

最近在研究react项目,接触到webpack打包工具.刚接触的时候一脸茫然,经过最近的学习,下面我来带大家开启webpack入门之旅. webpack是什么 webpack是近期最火的一款模块加载器兼打包工具,它能把各种资源,例如JS(含JSX).样式(含less/sass).图片等都作为模块来使用和处理.当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成少量的 bundle - 通常只

转载:在 ASP.NET 環境下使用 Memcached 快速上手指南

之前一直想研究 Memcached,這幾天花了些時間研究Memcached Providers 好讓我現有的 ASP.NET 專案能解決多台主機間快取不同步的狀況, 想不到花沒多少時間就上手了,也因此做了一些記錄. 安裝 Memcached (ver 1.4.4) for Win32 1. 下載 memcached 1.4.4 Windows 32-bit binary 或 memcached Windows 64-bit pre-release 2. 在 C:\Program Files 建立

开源Linux容器 OpenVZ 快速上手指南

Linux 容器及OpenVZ 简介 OpenVZ 安装 安装内核镜像 配置sysctl 以及安装相关工具 重启至OpenVZ 内核 简单上手 下载预先准备的模板也可以称作镜像 创建以及配置容器信息 开始运行以及使用 Linux 容器及OpenVZ 简介 Linux 容器(Linux Container,LXC)是一种轻量级的虚拟运行环境,它与传统的虚拟机方 式不同.传统的虚拟机需要对硬件作出抽象并提供了一个完整的操作系统,而LXC 不同的 地方就是,多个容器都与系统共用一套内核机制,而容器中提