【Unity Shaders】Using Textures for Effects——打包和混合textures
本系列主要参考《Unity Shaders and Effects
Cookbook》一书(感谢原书作者),同时会加上一点个人理解或拓展。
这里是本书所有的插图。这里是本书所需的代码和资源(当然你也可以从官网下载)。
========================================== 分割线 ==========================================
题外话
这次的内容是打包合并textures。
Textures不仅仅可以用来存储颜色信息,还可以存储很多数据信息。这些数据信息可以分别存储到R、G、B、A四个部分,然后再打包成一张Texture,像下图这样:
为什么这样做有好处呢?在我们的应用中,textures的数目将很大程度上影响应用的性能。因此,为了减少textures的数量,我们可以看看Shader中使用的那些图片可以合并成一张,以此来优化性能。
任何灰度图都可以被打包进另一个新的texture的RGBA四个中的某一个chanel。这听起来不是很明白,没关系,这篇文章就会展示如何做到这点。
一个常用的场景是,你想要混合多张textures到一个surface上。这在terrain Shaders(地形渲染)中很常见,这种时候你往往需要很好的将一张texture和另一张混合起来。
这篇文章中将会告诉你,怎样完成一个由4张textures混合渲染而得的terrain Shader。
开始工作
- 还是创建一个新的Shader文件,并为这个Shader创建一个新的Material,名字可以称为TextureBlending;
- 创建一个新的场景,以便来测试我们的Shader;
- 接下来,你需要4张用于混合的textures。它们可以是任何图片,但是为了得到一个效果较好的terrain Shader,建议你分别准备一张草地(grass)、泥土(dirt)、小石子(rocky dirt)、石头(rock)的texture。书的资源中包含了这样四张texture(Unity
Shaders and Effects Cookbook/5084_Code/Unity assets/5084_02_UnityAssets/Textures目录下的Chapter02_Grass0103_2_S.jpg,Chapter02_RockSmooth0045_2_S.jpg,Chapter02_SandPebbles0027_1_S.jpg,Chapter02_SandPebbles0030_3_S.jpg):
- 最后,也是奇妙所在,我们需要一张混合的texture(Unity Shaders and Effects Cookbook/5084_Code/Unity assets/5084_02_UnityAssets/Textures目录下的Chapter02_TerrainBlend_001.jpg),它是由多个灰度图混合而成的。它将会告诉我们以上四张texture在目标地形上是如何分布的:
实现
- 首先,向Shader的Properties块中添加一些新的properties。我们需要5个sampler2D对象,也就是textures,以及两个颜色properties,和一个用于调整整体地形颜色的值。
Properties {
_MainTint ("Diffuse Tint", Color) = (1,1,1,1) //Add the properties below so we can input all of our textures
_ColorA ("Terrain Color A", Color) = (1,1,1,1)
_ColorB ("Terrain Color B", Color) = (1,1,1,1)
_RTexture ("Red Channel Texture", 2D) = ""{}
_GTexture ("Green Channel Texture", 2D) = ""{}
_BTexture ("Blue Channel Texture", 2D) = ""{}
_ATexture ("Alpha Channel Texture", 2D) = ""{}
_BlendTex ("Blend Texture", 2D) = ""{}
} - 在SubShader中创建8个变量,分别对应Properties中的8个properties,以建立和它们之间的链接。
CGPROGRAM
#pragma surface surf Lambert float4 _MainTint;
float4 _ColorA;
float4 _ColorB;
sampler2D _RTexture;
sampler2D _GTexture;
sampler2D _BTexture;
sampler2D _BlendTex;
sampler2D _ATexture; - 为了根据每一张不同的texture来改变其在地形上的tiling rates(平铺率,可以理解为地上某些区域草比较密集,某些地区石头比较多等),我们需要修改结构体。
struct Input {
float2 uv_RTexture;
float2 uv_GTexture;
float2 uv_BTexture;
float2 uv_ATexture;
float2 uv_BlendTex;
}; - 在surf函数中,得到每张texture的信息,并分别存储在它们对应的变量中。
//Get the pixel data from the blend texture
//we need a float 4 here because the texture
//will return R,G,B,and A or X,Y,Z, and W
float4 blendData = tex2D(_BlendTex, IN.uv_BlendTex); //Get the data from the textures we want to blend
float4 rTexData = tex2D(_RTexture, IN.uv_RTexture);
float4 gTexData = tex2D(_GTexture, IN.uv_GTexture);
float4 bTexData = tex2D(_BTexture, IN.uv_BTexture);
float4 aTexData = tex2D(_ATexture, IN.uv_ATexture); - 使用lerp函数将四张texture混合。lerp函数有三个参数,lerp(value
: a, value : b, blend : c)。它从前两个参数中得到数据,并使用最后一个参数混合前两个值。//No we need to contruct a new RGBA value and add all
//the different blended texture back together
float4 finalColor;
finalColor = lerp(rTexData, gTexData, blendData.g);
finalColor = lerp(finalColor, bTexData, blendData.b);
finalColor = lerp(finalColor, aTexData, blendData.a);
finalColor.a = 1.0; - 最后,我们使用blending texture的R通道值混合两个颜色色调值,并将结果与之前的混合值相乘。
//Add on our terrain tinting colors
float4 terrainLayers = lerp(_ColorA, _ColorB, blendData.r);
finalColor *= terrainLayers;
finalColor = saturate(finalColor); o.Albedo = finalColor.rgb * _MainTint.rgb;
o.Alpha = finalColor.a;
Shader "Custom/TextureBlending" {
Properties {
_MainTint ("Diffuse Tint", Color) = (1,1,1,1) //Add the properties below so we can input all of our textures
_ColorA ("Terrain Color A", Color) = (1,1,1,1)
_ColorB ("Terrain Color B", Color) = (1,1,1,1)
_RTexture ("Red Channel Texture", 2D) = ""{}
_GTexture ("Green Channel Texture", 2D) = ""{}
_BTexture ("Blue Channel Texture", 2D) = ""{}
_ATexture ("Alpha Channel Texture", 2D) = ""{}
_BlendTex ("Blend Texture", 2D) = ""{}
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200 CGPROGRAM
#pragma surface surf Lambert float4 _MainTint;
float4 _ColorA;
float4 _ColorB;
sampler2D _RTexture;
sampler2D _GTexture;
sampler2D _BTexture;
sampler2D _BlendTex;
sampler2D _ATexture; struct Input {
float2 uv_RTexture;
float2 uv_GTexture;
float2 uv_BTexture;
float2 uv_ATexture;
float2 uv_BlendTex;
}; void surf (Input IN, inout SurfaceOutput o) {
//Get the pixel data from the blend texture
//we need a float 4 here because the texture
//will return R,G,B,and A or X,Y,Z, and W
float4 blendData = tex2D(_BlendTex, IN.uv_BlendTex); //Get the data from the textures we want to blend
float4 rTexData = tex2D(_RTexture, IN.uv_RTexture);
float4 gTexData = tex2D(_GTexture, IN.uv_GTexture);
float4 bTexData = tex2D(_BTexture, IN.uv_BTexture);
float4 aTexData = tex2D(_ATexture, IN.uv_ATexture); //No we need to contruct a new RGBA value and add all
//the different blended texture back together
float4 finalColor;
finalColor = lerp(rTexData, gTexData, blendData.g);
finalColor = lerp(finalColor, bTexData, blendData.b);
finalColor = lerp(finalColor, aTexData, blendData.a);
finalColor.a = 1.0; //Add on our terrain tinting colors
float4 terrainLayers = lerp(_ColorA, _ColorB, blendData.r);
finalColor *= terrainLayers;
finalColor = saturate(finalColor); o.Albedo = finalColor.rgb * _MainTint.rgb;
o.Alpha = finalColor.a;
}
ENDCG
}
FallBack "Diffuse"
}
我们新建一个地形,并把创建的Material赋给它后,可以看到以下效果:
解释
例如,我们想要在1和2之间找到一个中间值,我们可以使用0.5作为第三个参数,那么它将会返回1.5。因为一张texture的四个通道RGBA值都是简单的float类型,取值范围在-到1,因此可以使用它们作为混合程度值,即lerp的第三个参数来完成我们混合texture的需要。
在Shader中,我们仅从blend texture的四个通道中选择一个来控制每个像素颜色的混合结果。例如,我们从grass texture和dirt texture中提取颜色值,并使用blend texture对应的G通道值进行lerp运算。
如果可视化上述计算,可以参见下图:
Shader可以如此简单地使用blend texture的四个通道值,以及其他用于颜色的texture(如grass texture等),来创建出最后的混合而得的texture。这个最后的texture成为我们最终的地形颜色,并会和diffuse light(上述代码中的_MainTint变量)进行乘法运算,来得到最终效果。
细心的话,你可能会好奇上述代码中的两个颜色值_ColorA和_ColorB的用途是什么。从代码里可以看出来我们使用了blend texture的R通道值用于混合这两个颜色值,并和之前4张texture的混合结果相乘,这两个颜色值混合的结果可以看成是该地形本身的颜色,例如有红土地、黄土地、黑土地之类的区别。
扩展——灰度图
- 通道中的纯白,代表了该色光在此处为最高亮度,亮度级别是1。
- 通道中的纯黑,代表了该色光在此处完全不发光,亮度级别是0。
- 介于纯黑纯白之间的灰度,代表了不同的发光程度,亮度级别介于0至1之间。
- 灰度中越偏白的部分,表示色光亮度值越高,越偏黑的部分则表示亮度值越低。
value : b, blend : c)计算时,当我们使用R通道值作为第三个参数时,R通道灰度图中越亮的部分(值越接近1),在地形表现中越接近值b的结果。在上述代码中,使用R通道混合的是两个颜色值_ColorA和_ColorB。当我们取消其他的影响时,并设置两个颜色值左图所示时,我们可以预测R灰度图中越亮的部分对应到地形中则越接近白色(_ColorB),反之越暗的部分越接近红色(_ColorA),如右图所示:
【Unity Shaders】Using Textures for Effects——打包和混合textures的更多相关文章
- 【Unity Shaders】Using Textures for Effects介绍
本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...
- 【Unity Shaders】Using Textures for Effects —— 实现Photoshop的色阶效果
本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...
- 【Unity Shaders】Using Textures for Effects——让sprite sheets动起来
本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...
- 【Unity Shaders】Using Textures for Effects——通过修改UV坐标来滚动textures
本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...
- 【Unity Shaders】使用Unity Render Textures实现画面特效——画面特效中的亮度、饱和度和对照度
本系列主要參考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同一时候会加上一点个人理解或拓展. 这里是本书全部的插图. 这里是本书所需的代码 ...
- 【Unity Shaders】《Unity Shaders and Effects Cookbook》总结篇
我的唠叨 不知不觉,从发表第一篇关于<Unity Shaders and Effects Cookbook>已经快十个月了.一开始的初衷就是学习笔记,毕竟将来回过头去看的时候,再看英文难免 ...
- 【Unity Shaders】使用Unity Render Textures实现画面特效——建立画面特效脚本系统
本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...
- 【Unity Shaders】Mobile Shader Adjustment—— 什么是高效的Shader
本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...
- 【Unity Shaders】Lighting Models —— 衣服着色器
本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...
随机推荐
- JAVA局部内部类
在刚刚学到的android开发中了解到Button的onClick是通过局部内部类的方式实现的,具体的原理我以后再去了解,只是遇到问题总是想知道为什么,不要告诉我这是规则,死记住就可以了. 问题是局部 ...
- 背包DP入门小笔记01背包
FJUT OJ 2347 http://59.77.139.92/Problem.jsp?pid=2347 采药 TimeLimit:1000MS MemoryLimit:128MB 64-bit ...
- try_files 居然有这种用法
try_files 参考:https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/ Use the tr ...
- 防止SpringMVC拦截器拦截js等静态资源文件
SpringMVC提供<mvc:resources>来设置静态资源,但是增加该设置如果采用通配符的方式增加拦截器的话仍然会被拦截器拦截,可采用如下方案进行解决: 方案一.拦截器中增加针对静 ...
- 【我的书】《Unity Shader入门精要》出版上市
重要的事 先说重要的事,就是我的书籍<Unity Shader入门精要>在经过无数次跳票后,终于出版上市了(泪目-)! 购买传送门: 亚马逊 当当 京东 截止到我写这篇文章的时候,京东是没 ...
- 为什么选择C++
为什么选择C++,怎么不选其它语言呢? 为什么不选择C? 因为C++比C简单点~ 为什么不选择C#? 因为C++可以在所有操作系统上使用. 为什么不选择JAVA? 因为C++的性能好一点~ 还有其他的 ...
- 操作系统服务:OS模块
http://blog.csdn.net/pipisorry/article/details/52454486 一般的操作系统服务之OS模块Generic Operating System Servi ...
- svn数据仓库配置,权限配置
svn服务的开启有两种方式, · 自带的svnserve服务(访问地址类似于svn://192.168.80.18/repos/) · 与Apache配合使用 (访问地址 ...
- JSP1.x 自定义标签
Tag接口 任何一个标签都对应着一个java类,该类必须实现Tag接口,JSP遇到一个标签后后,将通过一个tld文件查找该标签的实现类,并运行该类的相关方法 import javax.servlet. ...
- Linux之dmesg命令
功能说明:显示内核缓冲区系统控制信息的工具 ,比如系统在启动时的信息会写到/var/log/中.语 法:dmesg [-cn][-s <缓冲区大小>] 补充说明:kernel会将开机信息存 ...