• 【Unity Shaders】Using Textures for Effects —— 实现Photoshop的色阶效果


    本系列主要参考《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;



  • 相关阅读:
    hadoop-2.7.6源码学习之 -- HDFS_Client端FileSystem获取
    Hadoop伪分布式搭建
    Hadoop技术集
    spark2.0源码学习
    hive资料
    Wiki动画回顾系列序&&目录
    阴暗的底层阶级版罗密欧与朱丽叶——甲贺忍法帖
    【搬家】夏令营感想
    “破晓之战”星杯传说赛后复盘分析
    leetcode刷题总结一
  • 原文地址:https://www.cnblogs.com/xiaowangba/p/6314706.html
Copyright © 2020-2023  润新知