本系列主要参考《Unity Shaders and Effects Cookbook》一书(感谢原书作者),同时会加上一点个人理解或拓展。

这里是本书所有的插图。这里是本书所需的代码和资源(当然你也可以从官网下载)。

========================================== 分割线 ==========================================

考虑到法向贴图(normal mapping)和程序贴图纹理(procedural textures)两节的内容要么很常见或者很少使用,因此我决定偷懒先不看了。

写在前面

终于到了Using Textures for Effects的最后一节!这次主要讲一个小技巧,用Shader实现Photoshop的色阶效果。

如果你曾经做过图像编辑工作,例如给你们家的一张合影调色啊,制作游戏贴等,那么你一定明白使用色阶来全局调整图像的重要性和实用性。现在,可以告诉你,你完全可以使用Shaders来创建类似Photoshop色阶的效果。

Photoshop里所有的图像编辑工具和混合模型就是一系列数学操作的结果。从本质上来说,我们是在对像素值与其他某些量进行乘法、加法、除法、或者比较等操作,来得到最后的返回值。这个返回值就是新图像的一个像素值。

当然,我们可以写一大本书来仅仅描述Photoshop效果使用的数学技巧,但这里,我们仅仅关注色阶这一块。在本书的第十章,Screen Effects with Unity Render Textures中,将会涉及更多的高级混合模式。(噢,第十章感觉好远的样子。)

准备工作

和以前一样,你需要创建一个新的Shader和Material,可以叫做PhotoshopLevels,并把使用了Shader的Material赋给场景中的某一个物体。除此之外,你还需要一个原帖图,来测试我们的色阶效果。你也可以使用本书资源中的/Unity Shaders and Effects Cookbook/5084_Code/Unity assets/5084_02_UnityAssets/Textures/Chapter02_MetalFloorsRusted0041_1_S.jpg。如下图所示:

实现

  1. 向Shader添加下列新的properties:
    	Properties {
    _MainTex ("Base (RGB)", 2D) = "white" {} //Add the Input Levels Values
    _inBlack ("Input Black", Range(0, 255)) = 0
    _inGamma ("Input Gamma", Range(0, 2)) = 1.61
    _inWhite ("Input White", Range(0, 255)) = 255 //Add the Output Levels
    _outWhite ("Output White", Range(0, 255)) = 255
    _outBlack ("Output Black", Range(0, 255)) = 0
    }
  2. 在CGPROGRAM命令下声明上述各个properties的变量:
    		CGPROGRAM
    #pragma surface surf Lambert sampler2D _MainTex; //Add these variables
    //to the CGPROGRAM
    float _inBlack;
    float _inGamma;
    float _inWhite;
    float _outWhite;
    float _outBlack;
  3. 创建一个新的函数float GetPixelLevel(float pixelColor),它的参数为原帖图RGB通道中一个通道的像素值,返回经过调整色阶后的新的该通道像素值。
    		float GetPixelLevel(float pixelColor)
    {
    float pixelResult;
    pixelResult = (pixelColor * 255.0);
    pixelResult = max(0, pixelResult - _inBlack);
    pixelResult = saturate(pow(pixelResult / (_inWhite - _inBlack), _inGamma));
    pixelResult = (pixelResult * (_outWhite - _outBlack) + _outBlack)/255.0;
    return pixelResult;
    }
  4. 修改surf函数,重新计算RGB通道的像素值。
                            //Create a variable to store
    //a pixel channel from our _MainTex texture
    float outRPixel = GetPixelLevel(c.r); float outGPixel = GetPixelLevel(c.g); float outBPixel = GetPixelLevel(c.b);
  5. 最后,输出新的像素值。
                            o.Albedo = float3(outRPixel,outGPixel,outBPixel);
    o.Alpha = c.a;
最后完整的代码如下:
Shader "Custom/PhotoshopLevels" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {} //Add the Input Levels Values
_inBlack ("Input Black", Range(0, 255)) = 0
_inGamma ("Input Gamma", Range(0, 2)) = 1.61
_inWhite ("Input White", Range(0, 255)) = 255 //Add the Output Levels
_outWhite ("Output White", Range(0, 255)) = 255
_outBlack ("Output Black", Range(0, 255)) = 0
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200 CGPROGRAM
#pragma surface surf Lambert sampler2D _MainTex; //Add these variables
//to the CGPROGRAM
float _inBlack;
float _inGamma;
float _inWhite;
float _outWhite;
float _outBlack; struct Input {
float2 uv_MainTex;
}; float GetPixelLevel(float pixelColor)
{
float pixelResult;
pixelResult = (pixelColor * 255.0);
pixelResult = max(0, pixelResult - _inBlack);
pixelResult = saturate(pow(pixelResult / (_inWhite - _inBlack), _inGamma));
pixelResult = (pixelResult * (_outWhite - _outBlack) + _outBlack)/255.0;
return pixelResult;
} void surf (Input IN, inout SurfaceOutput o) {
half4 c = tex2D (_MainTex, IN.uv_MainTex); //Create a variable to store
//a pixel channel from our _MainTex texture
float outRPixel = GetPixelLevel(c.r); float outGPixel = GetPixelLevel(c.g); float outBPixel = GetPixelLevel(c.b); o.Albedo = float3(outRPixel,outGPixel,outBPixel);
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}

前后效果对比如下所示(左边为使用Diffuse Shader,右边为使用PhotoshopLevels Shader):

解释

以float outRPixel  = GetPixelLevel(c.r);一句为例。
GetPixelLevel函数接受c.r作为输入参数。由于tex2D得到的值范围在0.0到1.0之间,我们需要将其映射到0.0到255.0之间:
pixelResult = (pixelColor * 255.0);

然后,我们减去_inBlack属性值,来使像素变暗。我们还要确保减去后的值不会小于0.0,因此使用max函数。

pixelResult = max(0, pixelResult - _inBlack);

接下来,我们用(_inWhite - _inBlack)来得到新的white point值,并和pixelResult做除法。这相比与我们直接除以_inWhite,会调高像素的亮度,使它变得更亮。 然后使用_inGamma进行乘方操作。

pixelResult = saturate(pow(pixelResult / (_inWhite - _inBlack), _inGamma));

最后,我们使用_outWhite和_outBlack来修改像素值,以便可以从全局上控制最小像素值(_outBlack)和最大像素值(_outWhite)。得到的结果需要除以255.0,来使得输出值的范围重新映射到0.0到1.0。

pixelResult = (pixelResult * (_outWhite - _outBlack) + _outBlack)/255.0;

【Unity Shaders】Using Textures for Effects —— 实现Photoshop的色阶效果的更多相关文章

  1. 【Unity Shaders】ShadowGun系列之一——飞机坠毁的浓烟效果

    写在前面 最近一直在思考下面的学习该怎么进行,当然自己有在一边做项目一边学OpenGL,偶尔翻翻论文之类的.但是,写shader是一个需要实战和动手经验的过程,而模仿是前期学习的必经之路.很多人都会问 ...

  2. 【Unity Shaders】Using Textures for Effects介绍

    本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...

  3. 【Unity Shaders】Using Textures for Effects——打包和混合textures

    本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...

  4. 【Unity Shaders】Using Textures for Effects——让sprite sheets动起来

    本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...

  5. 【Unity Shaders】Using Textures for Effects——通过修改UV坐标来滚动textures

    本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...

  6. 【Unity Shaders】使用Unity Render Textures实现画面特效——建立画面特效脚本系统

    本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...

  7. 【Unity Shaders】使用Unity Render Textures实现画面特效——画面特效中的亮度、饱和度和对照度

    本系列主要參考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同一时候会加上一点个人理解或拓展. 这里是本书全部的插图. 这里是本书所需的代码 ...

  8. 【Unity Shaders】《Unity Shaders and Effects Cookbook》总结篇

    我的唠叨 不知不觉,从发表第一篇关于<Unity Shaders and Effects Cookbook>已经快十个月了.一开始的初衷就是学习笔记,毕竟将来回过头去看的时候,再看英文难免 ...

  9. 【Unity Shaders】Lighting Models —— 衣服着色器

    本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...

随机推荐

  1. 吴恩达深度学习第2课第3周编程作业 的坑(Tensorflow+Tutorial)

    可能因为Andrew Ng用的是python3,而我是python2.7的缘故,我发现了坑.如下: 在辅助文件tf_utils.py中的random_mini_batches(X, Y, mini_b ...

  2. ZOJ-2965

    Accurately Say "CocaCola"! Time Limit: 2 Seconds      Memory Limit: 65536 KB In a party he ...

  3. 剑指架构师系列-持续集成之Maven+Nexus+Jenkins+git+Spring boot

    1.Nexus与Maven 先说一下这个Maven是什么呢?大家都知道,Java社区发展的非常强大,封装各种功能的Jar包满天飞,那么如何才能方便的引入我们项目,为我所用呢?答案就是Maven,只需要 ...

  4. FJUT寒假作业第二周G题解快速幂

    题目来源:http://210.34.193.66:8080/vj/Contest.jsp?cid=161#P6     题意:求n个数字的乘积对c取摸.主要就是有快速幂扩展到广义幂的过程. 首先题目 ...

  5. DOS/Windows 文本格式与 Unix 文本各式转换

    命令简介: dos2unix是将Windows格式文件转换为Unix.Linux格式的实用命令.Windows格式文件的换行符为\r\n ,而Unix&Linux文件的换行符为\n. dos2 ...

  6. UIkit复习:UIContorl及子控件的剖析

    1.模块继承关系: 1.UIButton        ->UIControl  -> UIView 2.UILabel          ->UIview 3.UIImageVie ...

  7. 安卓高级9 用原生intent分享

    大家都用过安卓app时发现有个分享按钮如下: 所以今天特此分享用用原生完成: package qianfeng.com.simplesharedemo; import android.content. ...

  8. proc文件系统探索 之 根目录下的文件[三]

    包括对proc根目录下meminfo文件的解析. > cat /proc/meminfo   读出的内核信息进行解释,下篇文章会简单对读出该信息的代码进行简单的分析. MemTotal: 507 ...

  9. 在Windows cmd中计算行数

    本文主体来自这篇外文文章的翻译.原文中有一个副标题:"如何简单地用Windows自带的FIND在CMD.exe中计算行数" 当我们在命令行环境中工作时,能计算其它工具输出内容的行数 ...

  10. 全废话SQL Server统计信息(1)——统计信息简介

    当心空无一物,它便无边无涯.树在.山在.大地在.岁月在.我在.你还要怎样更好的世界?--张晓风<我在> 为什么要写这个内容? 随着工作经历的积累,越来越感觉到,大量的关系型数据库的性能问题 ...