Shader学习笔记(二):Vertex/Fragment Shader

Shader 笔记二 片段着色器 Fragment Shader 简介 顶点片段着色器,运行于具有可编程渲染管线的硬件上,它包括顶点程序Vertex Programs和片段程序Fragment Programs。当在使用顶点程序或片段程序进行渲染的时候,图形硬件的固定功能管线会关闭,具体来说就是编写的顶点程序会替换掉固定管线中标准的3D变换,光照,纹理坐标生成等功能,而片段程序会替换掉SetTexture命令中的纹理混合模式。 顶点着色程序与片段着色程序通常... 阅读详情
AI摘要

本文围绕Unity中顶点、片元着色器(Vertex/Fragment Shader)展开。先介绍可编程渲染管线、Vertex Shader、Fragment Shader等关键概念及相关语法,接着通过Unity官方实例加深理解,最后探讨了Surface Shader与Vertex/Fragment Shader的编译关系。

这篇文章讨论如何写顶点、片元着色器(Vertex/Fragment Shader)。

概念解释

先看一个完整例子,关键地方我做了标记。先熟悉大致结构,后面我会详细解释:

Shader "Unlit/NewUnlitShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            // CG程序起始位置
            CGPROGRAM
            // 编译指令:表示有顶点着色器
            #pragma vertex vert
            // 编译指令:表示有片元着色器
            #pragma fragment frag
            #pragma multi_compile_fog

            #include "UnityCG.cginc"
            // 自定义的结构
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
            // 自定义的结构
            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            // 顶点着色器函数
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }
            // 片元着色器函数
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}
关键概念的理解
  • 可编程渲染管线:与固定渲染管线不同,可以让程序员自己控制渲染管线的一部分,分为Vertex Shader和Fragment Shader。
  • Vertex Shader:作用于每个顶点,通常是处理从世界空间到裁剪空间(屏幕坐标)的坐标转换,后面紧接的是光栅化。
  • Fragment Shader:作用于每个屏幕上的片元(这里可近似理解为像素),通常是计算颜色。
  • ShaderLab:Unity专有的shader语言,Surface Shader全部由ShaderLab实现,Vertex/Fragment是由ShaderrLab内嵌CG/HLSL实现。
  • Shading Language: 三种主流shader语言,均可以在ShaderLab中嵌套使用,官方推荐使用CG/HLSL(这两种语言由Microsoft和Nvidia达成一致,所以基本等价)。详见下面列表:
名字全名开发商嵌套关键字
CGC for GraphicNvidiaCGPROGRAM
HLSLHigh Level Shading LanguageMicrosoftCGPROGRAM
GLSLOpenGL Shading LanguageOpenGLGLSLPROGRAM

DirectX在Windows和游戏领域称霸,OpenGL则在移动领域胜出。
关于两者孰优孰劣的讨论数不胜数。
这里总结了DirectX在Windows平台胜出的一些原因:
1. DirectX更早推出可编程渲染管线
2. OpenGL成员太多,新标准推进缓慢
3. DirectX不仅是图形库,还包括声音、网络等一套游戏开发解决方案
4. DirectX不向后兼容,OpenGL为兼容性保留了很多过时的设计

Vertex/Fragment语法
  • SubShader:针对不同的硬件做不同的处理,运行时依次扫描,都失败则FallBack。
  • Pass:一个SubShader中包含1到多个Pass,通常只需一个Pass。多个Pass可用于定义多渲染路径(Rendering Path),运行时选择执行哪个,常见于自定义光照模型中阴影处理。
  • Shader Semantics:如float4 vertex : POSITION,冒号后面是这个变量的语义,与GPU交互时用到。
  • Vertex Shader的输入输出
    • 输入:可以是以下三种:
      • 由基本语义定义的变量:POSITION, NORMAL, TEXCOORD0, TEXCOORD1, …, TANGENT, COLOR。
      • 内置的结构:appdata_base, appdata_tan, appdata_full。
      • 自定义的结构。
    • 输出:固定有SV_POSITION,其他需要用到的varying变量。语义大部分情况下不重要,用TEXCOORD0就好。
  • Fragment Shader的输入输出
    • 输入:同Vertex Shader的输出。
    • 输出:SV_Target,通常是单个RGBA值。

实例解析

下面看几个Unity官方manual中的实例,加深理解:

  • SimpleUnlitTextureShader:简单的纹理贴图,变换顶点坐标,按uv找贴图上的颜色。

这里写图片描述

Shader "Unlit/SimpleUnlitTexturedShader"
{
    Properties
    {
        // we have removed support for texture tiling/offset,
        // so make them not be displayed in material inspector
        [NoScaleOffset] _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            // use "vert" function as the vertex shader
            #pragma vertex vert
            // use "frag" function as the pixel (fragment) shader
            #pragma fragment frag

            // vertex shader inputs
            struct appdata
            {
                float4 vertex : POSITION; // vertex position
                float2 uv : TEXCOORD0; // texture coordinate
            };

            // vertex shader outputs ("vertex to fragment")
            struct v2f
            {
                float2 uv : TEXCOORD0; // texture coordinate
                float4 vertex : SV_POSITION; // clip space position
            };

            // vertex shader
            v2f vert (appdata v)
            {
                v2f o;
                // transform position to clip space
                // (multiply with model*view*projection matrix)
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                // just pass the texture coordinate
                o.uv = v.uv;
                return o;
            }

            // texture we will sample
            sampler2D _MainTex;

            // pixel shader; returns low precision ("fixed4" type)
            // color ("SV_Target" semantic)
            fixed4 frag (v2f i) : SV_Target
            {
                // sample texture and return it
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
        }
    }
}
  • SingleColor:给模型赋予单色。vertex只传出SV_POSITION,fragment不传参。CG中直接引用了ShaderLab的Properties参数_Color(ShaderLab和CG的类型存在对应关系)。

这里写图片描述

Shader "Unlit/SingleColor"
{
    Properties
    {
        // Color property for material inspector, default to white
        _Color ("Main Color", Color) = (1,1,1,1)
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            // vertex shader
            // this time instead of using "appdata" struct, just spell inputs manually,
            // and instead of returning v2f struct, also just return a single output
            // float4 clip position
            float4 vert (float4 vertex : POSITION) : SV_POSITION
            {
                return mul(UNITY_MATRIX_MVP, vertex);
            }

            // color from the material
            fixed4 _Color;

            // pixel shader, no inputs needed
            fixed4 frag () : SV_Target
            {
                return _Color; // just return it
            }
            ENDCG
        }
    }
}
  • WorldSpaceNormal:根据法向量来变色。worldNormal作为varying变量传给fragment。

这里写图片描述

Shader "Unlit/WorldSpaceNormals"
{
    // no Properties block this time!
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // include file that contains UnityObjectToWorldNormal helper function
            #include "UnityCG.cginc"

            struct v2f {
                // we'll output world space normal as one of regular ("texcoord") interpolators
                half3 worldNormal : TEXCOORD0;
                float4 pos : SV_POSITION;
            };

            // vertex shader: takes object space normal as input too
            v2f vert (float4 vertex : POSITION, float3 normal : NORMAL)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                // UnityCG.cginc file contains function to transform
                // normal from object to world space, use that
                o.worldNormal = UnityObjectToWorldNormal(normal);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 c = 0;
                // normal is a 3D vector with xyz components; in -1..1
                // range. To display it as color, bring the range into 0..1
                // and put into red, green, blue components
                c.rgb = i.worldNormal*0.5+0.5;
                return c;
            }
            ENDCG
        }
    }
}
  • SkyReflection:反射环境。对比由Surface Shader的实现,可发现简洁很多。float4表示点,float3表示向量。

这里写图片描述

Shader "Unlit/SkyReflection"
{
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct v2f {
                half3 worldRefl : TEXCOORD0;
                float4 pos : SV_POSITION;
            };

            v2f vert (float4 vertex : POSITION, float3 normal : NORMAL)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                // compute world space position of the vertex
                float3 worldPos = mul(_Object2World, vertex).xyz;
                // compute world space view direction
                float3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
                // world space normal
                float3 worldNormal = UnityObjectToWorldNormal(normal);
                // world space reflection vector
                o.worldRefl = reflect(-worldViewDir, worldNormal);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the default reflection cubemap, using the reflection vector
                half4 skyData = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, i.worldRefl);
                // decode cubemap data into actual color
                half3 skyColor = DecodeHDR (skyData, unity_SpecCube0_HDR);
                // output it!
                fixed4 c = 0;
                c.rgb = skyColor;
                return c;
            }
            ENDCG
        }
    }
}

再谈与Surface Shader的关系

上一篇说到Surface Shader是简化版的shader编写工具,通过封装减少了程序员的重复工作量。
那么Surface Shader是如何编译成Vertex/Fragment Shader的呢?它们三者的先后关系又是怎样?

我的理解是:Surface Shader的surf函数位于fragment shader的起始阶段,编译后生成的是Fragment Shader。这点可用Unity自带的shader inspector工具查看。具体可参见知乎上的讨论,说得很详细了,这里不再展开。

【Unity Shaders】Vertex & Fragment Shader入门 写在前面 阅读详情

相关推荐

【OpenGL】Shader个人学习笔记--利用Shader画一个三角形

最常用的两种 shader : vertex shader OR fragment(pixel) shader data(CPU) -> GPU -> draw call -> shader Draw Call就是CPU调用图形编程接口,比如DirectX或OpenGL,来命令GPU进行渲染的操作。 vertex shader: 告诉 OpenGL 你想要 vertex 出现在屏幕空间的何处 fragment(pixel) shader : 栅格化,每个小三角形什么颜色 ...

qq_34878429的博客 758

Fragment Shader(片段着色器)

什么是 Fragment Shader(片段着色器)? 如果你曾经有用计算机绘图的经验,你就知道在这个过程中你需要画一个圆,然后一个长方形,一条线,一些三角形……直到画出你想要的图像。这个过程很像用手写一封信或一本书 —— 都是一系列的指令,需要你一件一件完成。 Shaders 也是一系列的指令,但是这些指令会对屏幕上的每个像素同时下达。也就是说,你的代码必须根据像素在屏幕上的不同位置执行不同的操作。就像活字印刷,你的程序就像一个 function(函数),输入位置信息,输出颜色信息,当它编译完之后会以相当

magic-zhu 4692

【Unity Shader实战】卡通风格的Shader(二)

写在前面好久没写博客了,一定是因为课程作业比较多,一定不是因为我懒,恩恩。三个月以前,在一篇讲卡通风格的Shader的最后,我们说到在Surface Shader中实现描边效果的弊端,也就是只对表面平缓的模型有效。这是因为我们是依赖法线和视角的点乘结果来进行描边判断的,因此,对于那些平整的表面,它们的法线通常是一个常量或者会发生突变(例如立方体的每个面),这样就会导致最后的效果并非如我们所愿。如下

candycat 3万+

unity ios GL_OES_depth_texture BlitCopyFromTexArray undeclared gl_FragDepthEXT 错误 ARC配置

刚刚运行起来就报下面错,各种查找,纠结,浪费一天时间记录一下解决方案: 2018-03-28 17:46:52.678011+0800 [260:7647] [DYMTLInitPlatform] platform initialization successful 2018-03-28 17:46:52.822916+0800 xxx[260:7505] -> registered...

atgczcl的专栏 1727

iOS --- OpenGLES之着色器(shader)的编译、链接及使用

在上一篇博客 iOS — OpenGLES之着色器(shader)语法介绍 中,简要介绍了OpenGLES的着色器shader的基本语法,以及Vertex Shader和Fragment Shader的差异。本文中,将简要介绍着色器(shader)的编译、链接及使用。

icetime17的专栏 3935

openGL学习笔记:着色器的理解,以及geometry shader的用法

在跟着learnopengl的教程(https://learnopengl.com/Advanced-OpenGL/Geometry-Shader)学习几何着色器(geometry shader)的时候,遇到了一些理解问题,现在解决了,记录一下。 当然中文我也不知道叫啥,但也差不多吧(希望不要差太多)。 在我今天学到进度里,关于着色器的学习有三种,vertex shader, fragment shader, geometry shader,着色器的作用是write data to the GPU ca

aDiligentCigarette的博客 1343

OpenGL学习(一)

绘制一个三角形

Crown__的博客 506

【OpenGL】Shader个人学习笔记(2)对Vertex 和 Fragment的命令行代码独立至单独的文件

Deal with Shaders in OpenGL 书接上文 CreateShader函数 take in 字符创指令生成shader std::string vertexShader = "#version 330 core\n" "\n" "layout(location = 0) in vec4 position;"// OpenGL的gl_Position是要求一个4维的vector "\n" "void mian()\n" "{\n" " gl_Position = p.

qq_34878429的博客 492

Vertex and Fragment Shader(可编程渲染管线)

一、引言 Vertex and Fragment Shader:最强大的Shader类型,下文中简称V&F Shader,属于可编程渲染管线. 使用的是CG/HLSL语法。分为2个部分vertex顶点部分和Fragment像素部分。 二、语法 1、例子如下 Shader "Custom/Exam1" { Properties { _MainTex ("Texture", 2D)

Champion_Lee的专栏 3574

Unity Shader 学习笔记(一)——Shader基础

一、什么是Shader? 简单来说shader就是一个告诉计算机以某种方式描绘物体的程序。 二、编写语言 使用Nvidia CG语言、微软HLSL语言编写,但都包含在Unity的 ShaderLab 语言中。 三、常用的Shader类型 Vertex&Fragment Shader(顶点片元着色器):是最基本的,也是非常强大的一种类型。一般用于2D的物体以及场景或者做一些特效。 Surface Shader:在系统编译后是一个复杂的Vertex&Fragment ..

L人青的博客 769

告别偏移地址!用C# S7PLUS驱动读写西门子S7-1500符号地址(TIA Portal V17+环境配置)

本文详细介绍了如何在TIA Portal V17+环境中使用C# S7PLUS驱动读写西门子S7-1500 PLC的符号地址,告别传统的偏移地址方式。通过配置OpenSSL、集成S7PLUS驱动库,以及实战案例展示符号地址的读写方法,显著提升代码可读性和维护效率。文章还涵盖了环境准备、高级配置及常见问题解决方案,助力工业自动化开发者实现更高效的PLC通讯。

weixin_28720767的博客 317

Unity Shaders Vertex & Fragment Shader入门

http://blog.youkuaiyun.com/candycat1992/article/details/40212735 三个月以前,在一篇讲卡通风格的Shader的最后,我们说到在Surface Shader中实现描边效果的弊端,也就是只对表面平缓的模型有效。这是因为我们是依赖法线和视角的点乘结果来进行描边判断的,因此,对于那些平整的表面,它们的法线通常是一个常量或者会发生突变(例如立方...

angle0524的博客 267

双向全桥隔离dc-dc变换器(DAB)

在全功率范围内,零电压开通优化调制方法能够实现双向全桥隔离dc-dc变换器所有开关的零电压开通,来提升变换器的效率,但其调制模式不连续。当变换器两端电压不匹配时,变换器的电流应力和回流功率将增加,软开关范围将变窄,变换器的效率显著降低。在轻载时,这两种电流优化调制方法采用相同的调制模式;在重载时,最小电流有效值调制方法具有两种调制模式,而最小电流应力调制方法只有一种。双向全桥隔离dc-dc变换器(DAB)和Buck/Boost不同,该变换器通过交流电感进行功率传输,其调制和控制不适合直接借鉴已有的研究。

m0_59329334的博客 3533

【Unity Shader编程】之十四 边缘发光Shader(Rim Shader)的两种实现形态

这篇文章主要讲解了如何在Unity3D中分别使用Surface Shader和Vertex & Fragment Shader实现边缘发光Shader。 一、最终实现的效果 边缘发光Shader比较直观的一个运用便是模拟宇宙中的星球效果。将本文实现的边缘发光Shader先赋一个Material,此Material再赋给一个球体,加上Galaxy Skybox, 实现的效果如下图: 当然,边缘发光Shader也可以实现例如暗黑三中精英怪的高亮效果,将一个怪物模型本身的Shader用边缘发光Sh

【浅墨的游戏编程Blog】毛星云(浅墨)的专栏 4万+

电机传动机构的转矩与惯量折算

参考《工业运动控制》

庐州牧 1万+

RTX 50系显卡(5080/5090D)CUDA 12.8环境下的vLLM部署避坑与实战指南

本文为RTX 5080/5090D显卡用户提供了在CUDA 12.8环境下部署vLLM的完整实战指南。文章详细介绍了从驱动安装、CUDA 12.8环境配置、PyTorch Nightly版本精准匹配,到vLLM源码编译及常见报错排查的全过程,帮助开发者高效利用新一代硬件进行大模型推理。

gray5的博客 667

[OpenGL] 通俗理解 什么是vertex shader 什么是 fragment shader(又称pixel shader)

shader是一段跑在GPU上的程序。 什么是vertex shader 什么是 fragment shader(又称pixel shader),作用: vertex shader:告诉显卡点应该被画在什么位置,对每一个顶点(这些顶点可能有很多属性(纹理,法线等等),也可能只有最基本的位置属性),假设有三个顶点,那么就应该被调用3次。 fragment shader(pixel shader):对每一个需要渲染的像素运行fragment shader,设置像素的颜色,并最终渲染在屏幕...

... 9380

在VSCode中使用LaTex+(grammarly语法检测,chatgpt翻译,Sumatra PDF 轻量级pdf阅读器)

整个文章分为以下几个内容,打 * 的是必须要安装的。

u011895157的博客 2万+

Unity Shader开发指南:Surface、V&F与Shader Graph核心解析

在计算机图形学中,着色器(Shader)是控制渲染管线中顶点和像素处理的核心程序,其原理在于通过GPU并行计算,将3D模型的几何与材质信息转换为屏幕上的2D图像。这项技术是实现游戏画面、影视特效和实时可视化等数字内容的关键。对于Unity开发者而言,掌握不同的Shader编写方式能极大提升渲染效果开发效率与灵活性。表面着色器(Surface Shader)作为Unity封装的高级抽象层,通过声明式编程快速实现基于物理的光照模型,适合标准材质开发;顶点片元着色器(Vertex & Fragment Shade

weixin_30385925的博客 416

大数据应用技术实验报告一 Hadoop三种模式安装配置

JDK+Hadoop安装配置、单机模式配置 以下操作在SecureCRT里面完成 1.关闭防火墙 firewall-cmd --state 显示防火墙状态running/not running systemctl stop firewalld 临时关闭防火墙,每次开机重新开启防火墙 systemctl disable firewalld 禁止防火墙服务。 2.传输JDK和HADOOP压缩包 Sec...

Ruthless_Childe的博客 3094

工业级稳定!C# 实现 Modbus RTU 多设备并发通信实战

摘要:本文提出一种基于C#的Modbus RTU多设备并发通信方案,解决工业自动化中单线程通信的串口抢占、数据阻塞等问题。采用任务队列+线程安全锁+异步调度的三层架构,实现16台及以上RTU设备的稳定并发读写。方案包含串口管理单例、Modbus通信核心和任务调度层,支持超时重试、异常捕获等工业级容错机制。通过NModbus库和.NET框架,提供可直接用于工业现场的代码实现,已在工控项目中长期验证,有效提升多设备通信效率和稳定性。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 581

Vertex & Fragment Shader入门

写在前面 三个月以前,在一篇讲卡通风格的Shader的最后,我们说到在Surface Shader中实现描边效果的弊端,也就是只对表面平缓的模型有效。这是因为我们是依赖法线和视角的点乘结果来进行描边判断的,因此,对于那些平整的表面,它们的法线通常是一个常量或者会发生突变(例如立方体的每个面),这样就会导致最后的效果并非如我们所愿。如下图所示: 因

linuxheik的专栏 1649

从零到一构建一个智能爬虫 Agent 完整教程

本文将打造一个具备自主决策、动态解析、反反爬、持久化能力的智能爬虫 Agent

数据知道的博客 4793

【Unity Shaders】学习笔记——Shader和渲染管线

Shader和渲染管线

喵~ 2336

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

Unity 中用 Vertex & Fragment Shader 实现 surface shader 中的 Diffuse 和 Decal

自己体验了以后发现surface shader确实是隐藏了好多好多的内部实现呀,像我没有考虑的多光源,阴影,衰减等问题,在surface shader中都是被隐藏实现好了的,而且还是多平台适配的,Vertex & Fragment Shader则还要自己写,还好unity也给我们提供了一些现成方法可以调。 _(:зゝ∠)_ 所以总结一下,在写需要接收复杂光源信息的材质时,写surface

勥小透明 3242

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

上一篇: 【游戏圈一周精选】:论网游数量与青少年视力的关系
下一篇: Shader学习笔记(三):Shader中的光照
长三月
博客等级 码龄9年 228粉丝 80原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值