Shader编程学习笔记(八)—— Surface Shader 2

Surface Shader

  上一小结主要了解了Surface Shader使用了“#pragma surface surf Standard fullforwardshadows”指令的意义,这一小节主要了解“surf”surface函数。

void surf (Input IN, inout SurfaceOutputStandard o) {
    // Albedo comes from a texture tinted by color
    fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
    o.Albedo = c.rgb;
    // Metallic and smoothness come from slider variables
    o.Metallic = _Metallic;
    o.Smoothness = _Glossiness;
    o.Alpha = c.a;
}

  surface函数总是无返回的,但在Unity当中要渲染物体,一个函数总是要输出最后的结果,这个"surf"函数没有返回结果,实际上它并没有以返回值的形式进行输出,它有两个参数:“Input IN”和“inout SurfaceOutputStandard o”。

  第一个参数“Input IN”就是前面定义的“Input”结构体,在这个结构体中当前只有主纹理的uv坐标。关于这个纹理坐标我们可以查看一下Unity手册中的Writing Surface Shaders,在“Surface Shader input structure”中描述了除了使用“uv”作为开头的成员外,还有其他的成员,这些主要用于光照的计算,关于这些内容的使用,只有在系统地学习Cg语法、Vertex & Fragment Shader程序设计和计算机图形学的一些经典光照算法后,才能更灵活地使用这些功能,目前我们只要了解在“Input”结构体当中的纹理uv坐标值的功能。

  第二个参数使用一个特性"inout",这个特性是Cg语言中比较重要的内容。如果在参数前没有任何修饰,那么默认是指输入的;用“out”修饰的,它的值在最后当做输出,并且在外部可以被直接使用。如果用“inout”,它描述的是这个参数既是输入的也是输出的。这个"surf"函数虽然没有返回值,但是它的第二个参数是有输出功能的。

  第二个参数的类型是“SurfaceOutputStandard”,关于这个类型可以查看一下官方手册,有一个结构体为“SurfaceOutput”,是Unity5.0之前使用的,而在Unity5.0当中,“SurfaceOutput”具有不同的形态,它演变成两种结构体,分别是“SurfaceOutputStandard”和“SurfaceOutputStandardSpecular”,它们能被用于基于物理的光照模型,从而Unity就具有了PBS(基于物理着色)特性。关于基于物理着色,很多现代引擎都在追求和实现,而真正实现了基于物理着色的引擎才可以被称之为次世代引擎,因为它可以从很大程度上去真实地模拟光线跟踪。Unity使用了美国迪士尼公司所使用的一种称为Cook-Torrance的BRDF模型,再加上全局光照的计算,因此在Unity5当中我们可以把场景和物体渲染得比以前的版本更真实、更漂亮。

  接下来简单了解一下“SurfaceOutput”和“SurfaceOutputStandard”两种结构体的对比。

struct SurfaceOutput
{
    fixed3 Albedo;  // diffuse color
    fixed3 Normal;  // tangent space normal, if written
    fixed3 Emission;
    half Specular;  // specular power in 0..1 range
    fixed Gloss;    // specular intensity
    fixed Alpha;    // alpha for transparencies
};

SurfaceOutput

struct SurfaceOutputStandard
{
    fixed3 Albedo;      // base (diffuse or specular) color
    fixed3 Normal;      // tangent space normal, if written
    half3 Emission;
    half Metallic;      // 0=non-metal, 1=metal
    half Smoothness;    // 0=rough, 1=smooth
    half Occlusion;     // occlusion (default 1)
    fixed Alpha;        // alpha for transparencies
};

SurfaceOutputStandard

  两个结构体都有“Albedo”,但是在Unity5以后,“Albedo”指的是基本的漫反射或者高光的颜色值,而在早期版本中,“Albedo”就只是漫反射颜色值;另外,两者都有“Normal”、“Emission”和“Alpha”值,这些两者都是一样的;但是早期版本有“Specular”,新版已经把它涵盖在“Albedo”当中了;早期版本有“Gloss”,而新版已经用“Smoothness”取代了;新版本有“Metallic”和“Occlusion”,“Metallic”用来显示金属化程度,“Occlusion”是一种剔除特性,这些早期版本都是没有的。

  为了将当前的Surface Shader精简化,我们可以把它改成旧版的Surface Shader。

Shader "Lesson/SurfaceShader2" {
    Properties {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM

        #pragma surface surf Lambert fullforwardshadows

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0

        sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
        };

        void surf (Input IN, inout SurfaceOutput o) {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

  其中删除了“_Color”、“_Glossiness”和“_Metallic”属性,将“#pragma surface surf Standard fullforwardshadows”改成“#pragma surface surf Lambert fullforwardshadows”。回到场景中,效果如图:

  可以观察到原先球体的金属光泽已经没有,球体仅仅只有漫反射的感觉,这就是早期Surface Shader的效果。

  在当前surface函数当中基本是一些赋值操作,“fixed4 c = tex2D (_MainTex, IN.uv_MainTex);”声明了一个颜色值,“tex2D”是Cg中的一个函数,它主要的功能就是从纹理中进行采样,使用了一个纹理坐标对纹理采样,得到了一个颜色值。然后将采样得到的颜色的rgb值交给了“SurfaceOutput”的“Albedo”值,并将得到颜色的alpha值交给了“SurfaceOutput”的“alpha”值。

  在大概了解了surface函数的意义后,可以考虑将贴图调整成基于灰度来获取alpha通道,但是发现这个物体并没有透明效果,在Surface Shader当中,要达到透明效果,就不能使用先前使用过的alpha混合,启用一个blend选项来实现,因为Surface Shader没有pass通道,但是Unity官方指明了另外的方案,可以在Surface Shader文档的“Optional parameters”找到解决方案,如:

  • alpha or alpha:auto
  • alpha:blend
  • alpha:fade
  • alpha:premul
  • alphatest:VariableName
  • keepalpha
  • decal:add
  • decal:blend

  其中,“alpha or alpha:auto”的功能是会自动选择使用“alpha:fade”或者“alpha:premul”的功能,如果使用的是简单光照函数等同于使用“alpha:fade”,如果使用基于物体的光照函数等同于使用“alpha:premul”左乘运算;“alphatest:VariableName”可以指定一个变量,我们可以用这个变量自身的第四个分量,比如说一个颜色值的alpha值就可以取代真正的"alphatest";“keepalpha”表示保持alpha值;“decal:add”和“decal:blend”都是用来描述印花的方案,其中“decal:add”实际上就是“blend”当中源和目标混合的one和one。
  接下来可以先尝试使用“alpha”,将它放在“pragma”附加的指令部分:“#pragma surface surf Lambert fullforwardshadows alpha”。回到工程中,效果如图:

  可以发现球体有了变化,似乎有透明的感觉,但是让人感觉很不自然,上半部分被削了一块,原因是这里没有指定它的渲染序列,需要在Tags当中添加一个渲染序列:“Tags { "RenderType"="Opaque" "queue"="transparent"}”,指定一个“transparent”,表示它是一个透明物体。

  回到场景中,重新给物体选择一下shader,编译通过后可以发现球体已经透明了,效果如图:

  对于一个透明物体,有时并不需要它产生阴影,把阴影选项“fullforwardshadows”删除,回到场景中发现阴影还存在,这是因为shader的“Fallback”里有一个“Diffuse”,如果把“Fallback”注释掉,可以发现场景里球体的阴影消失了。因此,可以发现“Fallback”有一个很好的用途,当Surface Shader编译后没有得到一个阴影投射器时,通过“Fallback”会自动在指定的具有阴影投射器通道的shader中(这里是“Diffuse”)去选择这个阴影投射器使用,这就是“Fallback”的一个妙用。

  如果没有“Fallback”,又希望物体有阴影效果,可以在“pragma”中增加一个阴影投射通道“addshadow”:“#pragma surface surf Lambert alpha addshadow”。回到场景中,发现物体并没有投射阴影,原因是如果一个具有了alpha混合的半透明物体去投射一个阴影是不能得到最终需要的阴影效果的,因为有可能会有其他的物体光线投射并且透过这个物体落到这个物体的阴影范围内,让这个物体去投射一个实体的阴影不符合半透明物体的阴影形象。要产生阴影,需要删除"pragma"的“alpha”选项,取消“transparent”渲染序列,然后回到场景中,就可以到看到阴影效果了,不过这个球体就不透明了。代码以及效果如下:

Shader "Lesson/SurfaceShader2" {
    Properties {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
    }
    SubShader {
        Tags { "RenderType"="Opaque"}
        LOD 200

        CGPROGRAM

        #pragma surface surf Lambert addshadow
        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0

        struct Input {
            float2 uv_MainTex;
        };

        sampler2D _MainTex;

        void surf (Input IN, inout SurfaceOutput o) {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        ENDCG
    }
    // FallBack "Diffuse"
}

时间: 2024-07-31 12:29:45

Shader编程学习笔记(八)—— Surface Shader 2的相关文章

Shader编程学习笔记(七)—— Surface shader 1

Surface shader 本小结对Unity的Surface Shader做一个大概的了解.主要了解在Surface Shader当中比较重要的几个部分,分别是: SurfaceOutput Input lighing shadow 首先查看一下Unity的官方手册中的Writing Surface Shaders,其中描述道:如果要编写一个shader去和光进行交互是比较复杂的,因为光照会有不同的光照类型,不同的阴影选项和不同的渲染路径(包括foward和deferred renderin

Shader编程学习笔记(四)——Unity Shader的组织形式(ShaderLab)

Unity Shader的组织形式 Unity Shader的形态 Unity官方手册上讲Unity Shader有三种不同的编写方案,这三种编写方案分别是surface shaders.vertex and fragment shaders和fixed function shaders. 从前面几篇笔记中可以了解到,可编程图形管线中能够编写shader的主要是两个部分:vertex shader和fragment shader,但Unity还有surface shaders和fixed fun

Shader编程学习笔记(三)——三大主流编程语言 HLSL/GLSL/Cg

三大主流编程语言 HLSL/GLSL/Cg Shader Language Shader Language的发展方向是设计出在便携性方面可以和C++.Java等相比的高级语言,“赋予程序员灵活而方便的编程方式”,并“尽可能的控制渲染过程”同时“利用图形硬件的并行性,提高算法效率”. Shader Language目前主要有3种语言:基于OpenGL的OpenGL Shading Language,简称GLSL;基于DirectX的High Level Shading Language,简称HLS

Shader编程学习笔记(一)—— 图形硬件简史与可编程管线

图形处理器(GPU)简史 GPU发展简史 GPU英文全称Graphic Processing Unit,中文翻译为“图形处理器”,在现代计算机系统中的作用变得越来越重要. 20世纪六.七十年代,受硬件条件的限制,图形显示器只是计算机输出的一种工具.限于硬件发展水平,人们只是纯粹从软件实现的角度来考虑图形用户界面的规范问题.此时还没有GPU的概念. GPU概念在20世纪70年代末和80年代初被提出,使用单片集成电路(monolithic)作为图形芯片,此时的GPU被用于视频游戏和动画方面,它能够很

Shader编程学习笔记(二)—— Shader和渲染管线

Shader和渲染管线 什么是Shader Shader,中文翻译即着色器,是一种较为短小的程序片段,用于告诉图形硬件如何计算和输出图像,过去由汇编语言来编写,现在也可以使用高级语言来编写.一句话概括:Shader是可编程图形管线的算法片段. 它主要分为两类:Vertex Shader和Fragment Shader. 什么是渲染管线 渲染管线也称为渲染流水线,是显示芯片内部处理图形信号相互独立的并行处理单元.一个流水线是一序列可以并行和按照固定顺序进行的阶段.就像一个在同一时间内,不同阶段不同

DirectX 11游戏编程学习笔记之7: 第6章Drawing in Direct3D(在Direct3D中绘制)(重点回顾+勘误)

        本文由哈利_蜘蛛侠原创,转载请注明出处!有问题欢迎联系[email protected]         注:我给的电子版是700多页,而实体书是800多页,所以我在提到相关概念的时候,会使用章节号而非页码.同样的情况适合于"龙书"第二版. 上一期的地址: DX 11游戏编程学习笔记之6 这一章应该是本书最长的一章了,可能也是最难的一章,所以大家一定要好好消化,仔细学习!这一章大致相当于"龙书"第二版的第7章和第8章,还添加了一些别的东西. 由于这一

图像编程学习笔记8——图像的平滑(去噪)

第一种方法:高斯模版 以下文字内容copy于<<数字图像处理编程入门>>,code为自己实现,是win32控制台程序. 先举个例子说明一下什么是平滑(smoothing),如下面两幅图所示:可以看到,图3.2比图3.1柔和一些(也模糊一些).是不是觉得很神奇?其实实现起来很简单.我们将原图中的每一点的灰度和它周围八个点的灰度相加,然后除以9,作为新图中对应点的灰度,就能实现上面的效果. 这么做并非瞎蒙,而是有其道理的.大概想一想,也很容易明白.举个例子,就象和面一样,先在中间加点水

多线程编程学习笔记——线程同步(三)

接上文 多线程编程学习笔记——线程同步(一) 接上文 多线程编程学习笔记——线程同步(二) 七.使用Barrier类 Barrier类用于组织多个线程及时在某个时刻会面,其提供一个回调函数,每次线程调用了SignalAndWait方法后该回调函数就会被执行. 1.代码如下: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; //

angular学习笔记(八)

本篇介绍angular控制视图的显示和隐藏: 通过给元素添加ng-show属性或者ng-hide属性来控制视图的显示或隐藏: ng-show: 绑定的数据值为true时,显示元素,值为false时,隐藏元素 ng-hide: 绑定的数据值为true时,隐藏元素,值为false时,显示元素 (其实只要用到其中一个就可以了) 下面来看个简单的例子,点击按钮可以显示/隐藏元素: <!DOCTYPE html> <html ng-app> <head> <title>