您的位置:首页 > 运维架构

Advanced ShaderLab topics

2015-09-10 10:54 453 查看

Implementing Fixed Function TexGen in Shaders

Before Unity 5,
texture properties could have options inside thecurly brace block, e.g.
TexGen CubeReflect
. These were controlling fixed functiontexture coordinate generation. This functionality was removed in Unity 5.0; if you needtexgen you should write a
vertex shader instead.

This page shows how to implement each of fixed function TexGen modes from Unity 4.

Cubemap reflection (TexGen CubeReflect)

TexGen CubeReflect
is typically used for simple
cubemap reflections.It reflects view direction along the normal in view space, and uses that as the UVcoordinate.



Shader "TexGen/CubeReflect" {
Properties {
    _Cube ("Cubemap", Cube) = "" { /* used to be TexGen CubeReflect */ }
}
SubShader { 
    Pass { 
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        #include "UnityCG.cginc"
        
        struct v2f {
            float4 pos : SV_POSITION;
            float3 uv : TEXCOORD0;
        };

        v2f vert (float4 v : POSITION, float3 n : NORMAL)
        {
            v2f o;
            o.pos = mul(UNITY_MATRIX_MVP, v);

            // TexGen CubeReflect:
            // reflect view direction along the normal,
            // in view space
            float3 viewDir = normalize(ObjSpaceViewDir(v));
            o.uv = reflect(-viewDir, n);
            o.uv = mul(UNITY_MATRIX_MV, float4(o.uv,0));
            return o;
        }

        samplerCUBE _Cube;
        half4 frag (v2f i) : SV_Target
        {
            return texCUBE(_Cube, i.uv);
        }
        ENDCG 
    } 
}
}

Cubemap normal (TexGen CubeNormal)

TexGen CubeNormal
is typically used with
cubemaps too.It uses view space normal as the UV coordinate.



Shader "TexGen/CubeNormal" {
Properties {
    _Cube ("Cubemap", Cube) = "" { /* used to be TexGen CubeNormal */ }
}
SubShader { 
    Pass { 
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        #include "UnityCG.cginc"
        
        struct v2f {
            float4 pos : SV_POSITION;
            float3 uv : TEXCOORD0;
        };

        v2f vert (float4 v : POSITION, float3 n : NORMAL)
        {
            v2f o;
            o.pos = mul(UNITY_MATRIX_MVP, v);

            // TexGen CubeNormal:
            // use view space normal of the object
            o.uv = mul((float3x3)UNITY_MATRIX_IT_MV, n);
            return o;
        }

        samplerCUBE _Cube;
        half4 frag (v2f i) : SV_Target
        {
            return texCUBE(_Cube, i.uv);
        }
        ENDCG 
    } 
}
}

Object space coordinates (TexGen ObjectLinear)

TexGen ObjectLinear
used object space vertex position as UV coordinate.



Shader "TexGen/ObjectLinear" {
Properties {
    _MainTex ("Texture", 2D) = "" { /* used to be TexGen ObjectLinear */ }
}
SubShader { 
    Pass { 
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        #include "UnityCG.cginc"
        
        struct v2f {
            float4 pos : SV_POSITION;
            float3 uv : TEXCOORD0;
        };

        v2f vert (float4 v : POSITION)
        {
            v2f o;
            o.pos = mul(UNITY_MATRIX_MVP, v);

            // TexGen ObjectLinear:
            // use object space vertex position
            o.uv = v.xyz;
            return o;
        }

        sampler2D _MainTex;
        half4 frag (v2f i) : SV_Target
        {
            return tex2D(_MainTex, i.uv.xy);
        }
        ENDCG 
    } 
}
}

View space coordinates (TexGen EyeLinear)

TexGen EyeLinear
used view space vertex position as UV coordinate.



Shader "TexGen/EyeLinear" {
Properties {
    _MainTex ("Texture", 2D) = "" { /* used to be TexGen EyeLinear */ }
}
SubShader { 
    Pass { 
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        #include "UnityCG.cginc"
        
        struct v2f {
            float4 pos : SV_POSITION;
            float3 uv : TEXCOORD0;
        };

        v2f vert (float4 v : POSITION)
        {
            v2f o;
            o.pos = mul(UNITY_MATRIX_MVP, v);

            // TexGen EyeLinear:
            // use view space vertex position
            o.uv = mul(UNITY_MATRIX_MV, v).xyz;
            return o;
        }

        sampler2D _MainTex;
        half4 frag (v2f i) : SV_Target
        {
            return tex2D(_MainTex, i.uv.xy);
        }
        ENDCG 
    } 
}
}

Spherical environment mapping (TexGen SphereMap)

TexGen SphereMap
computes UV coordinates for spherical environment mapping.See
OpenGL TexGen reference for the formula.



Shader "TexGen/SphereMap" {
Properties {
    _MainTex ("Texture", 2D) = "" { /* used to be TexGen SphereMap */ }
}
SubShader { 
    Pass { 
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        #include "UnityCG.cginc"
        
        struct v2f {
            float4 pos : SV_POSITION;
            float2 uv : TEXCOORD0;
        };

        v2f vert (float4 v : POSITION, float3 n : NORMAL)
        {
            v2f o;
            o.pos = mul(UNITY_MATRIX_MVP, v);

            // TexGen SphereMap
            float3 viewDir = normalize(ObjSpaceViewDir(v));
            float3 r = reflect(-viewDir, n);
            r = mul((float3x3)UNITY_MATRIX_MV, r);
            r.z += 1;
            float m = 2 * length(r);
            o.uv = r.xy / m + 0.5;

            return o;
        }

        sampler2D _MainTex;
        half4 frag (v2f i) : SV_Target
        {
            return tex2D(_MainTex, i.uv);
        }
        ENDCG 
    } 
}
}


Shader Level of Detail

Shader Level of Detail (LOD) works by only using shaders or subshaders that have their LOD value less than a given number.

By default, allowed LOD level is infinite, that is, all shaders that are supported by the user’s hardware can be used. However, in some cases you might want to drop shader details, even if the hardware can support them. For example, some cheap graphics cards
might support all the features, but are too slow to use them. So you may want to not use parallax normal mapping on them.

Shader LOD can be either set per individual shader (using
Shader.maximumLOD), or globally for all shaders (using
Shader.globalMaximumLOD).

In your custom shaders, use LOD command to set up LOD value for any subshader.

Built-in shaders in Unity have their LODs set up this way:

VertexLit kind of shaders = 100
Decal, Reflective VertexLit = 150
Diffuse = 200
Diffuse Detail, Reflective Bumped Unlit, Reflective Bumped VertexLit = 250
Bumped, Specular = 300
Bumped Specular = 400
Parallax = 500
Parallax Specular = 600

Platform Specific Rendering Differences

Unity runs on various platforms and in some cases there are differences in how things behave. Most of the time Unity hides the differences from you, but sometimes you can still bump into them.

Render Texture Coordinates

Vertical texture coordinate conventions differ between Direct3D-like and OpenGL-like platforms:

In Direct3D and Metal, the coordinate is zero at the top, and increases downwards.
In OpenGL and OpenGL ES, the coordinate is zero at the bottom, and increases upwards.

Most of the time this does not really matter, except when rendering into a
Render Texture. In that case, Unity internally flips rendering upside down when rendering into a texture on Direct3D, so that the conventions match between the platforms.

One case where this does not happen, is when
Image Effects and Anti-Aliasing is used. In this case, Unity renders to screen to get anti-aliasing, and then “resolves” rendering into a RenderTexture for further processing with an Image Effect. The resulting source texture for an image effect is
not flipped upside down on Direct3D (unlike all other Render Textures).

If your Image Effect is a simple one (processes one texture at a time) then this does not really matter because

Graphics.Blit takes care of that.

However, if you’re processing more than one RenderTexture together in your Image Effect, most likely they will come out at different vertical orientations (only in Direct3D-like platforms, and only when anti-aliasing is used). You need to
manually “flip” the screen texture upside down in your vertex shader, like this:

// On D3D when AA is used, the main texture and scene depth texture
// will come out in different vertical orientations.
// So flip sampling of the texture when that is the case (main texture
// texel size will have negative Y).

#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
        uv.y = 1-uv.y;
#endif

Check out the Edge Detection scene in the
Shader Replacement sample project for an example of this. Edge detection there uses both the screen texture and the Camera’s

Depth+Normals texture.

Semantics used by shaders

To get shaders working on all platforms, some special shader values should use these semantics:

Vertex shader output (clip space) position:
SV_POSITION
. Sometimes shaders use
POSITION
semantics for that, but this will not work on Sony PS4 and will not work when tessellation is used.
Fragment shader output color:
SV_Target
. Sometimes shaders use
COLOR
or
COLOR0
for that, but again that will not work on PS4.
When rendering meshes as Points, make sure to output
PSIZE
semantics output from the vertex shader (e.g. set it to 1). Some platforms (e.g. OpenGL ES or Metal) treat point size as “undefined” when it’s not written to from the shader.

AlphaTest and programmable shaders

Some platforms, most notably mobile (OpenGL ES & Metal) and Direct3D 11, do not have fixed function
alpha testing functionality. When you are using programmable shaders, it’s advisable to use the Cg/HLSL
clip()
function in the pixel shader instead.

Direct3D 9 / 11 shader compiler is more picky about syntax

Direct3D platforms use Microsoft’s HLSL shader compiler. The HLSL compiler is more picky than other compilers about various subtle shader errors. For example, it won’t accept function output values that aren’t initialized properly.

The most common places where you would run into this are:

A
Surface shader vertex modifier that has an “out” parameter. Make sure to initialize the output like this: void vert (inout appdata_full v, out Input o) {
UNITY_INITIALIZE_OUTPUT(Input,o); // … }

Partially initialized values, e.g. a function returns float4, but the code only sets the .xyz values of it. Make sure to set all values or else change to float3 if you only need three values.

Using
tex2D
in the vertex shader. This is not valid since UV derivatives don’t exist in the vertex shader; you need to sample an explicit mip level instead, e.g. use
tex2Dlod (tex, float4(uv,0,0))
. You’ll need to add
#pragma target 3.0
since tex2Dlod is a shader model 3.0 feature.

DirectX 11 HLSL syntax and Surface Shaders

Currently some parts of the
surface shader compilation pipeline do not understand DX11-specific HLSL syntax. If you’re using HLSL features like StructuredBuffers, RWTextures and other non-DX9 syntax, you have to wrap them in a DX11-only preprocessor macro:

#ifdef SHADER_API_D3D11
// DX11-specific code, e.g.
StructuredBuffer<float4> myColors;
RWTexture2D<float4> myRandomWriteTexture;
#endif

Using Shader Framebuffer Fetch

Some GPUs (most notably PowerVR based ones on iOS) allow doing a form of programmable blending, by providing current fragment color as input to the fragment shader (see

EXT_shader_framebuffer_fetch).

It is possible to write shaders in Unity that use the framebuffer fetch functionality. When writing HLSL/Cg fragment shader, just use
inout
color argument in it, for example:

CGPROGRAM
// only compile shader for platforms that can potentially
// do it (currently gles,gles3,metal)
#pragma only_renderers framebufferfetch

void frag (v2f i, inout half4 ocol : SV_Target)
{
    // ocol can be read (current framebuffer color)
    // and written into (will change color to that one)
    // ...
}   
ENDCG

iPad2 and MSAA and alpha-blended geometry

There is a bug in Apple driver resulting in artifacts when MSAA is enabled and alpha-blended geometry is drawn with non RGBA colorMask. To prevent artifacts we force RGBA colorMask when this configuration is encountered, though it will render built-in Glow
FX unusable (as it needs DST_ALPHA for intensity value). Also, please update your shaders if you wrote them yourself (see “Render Setup -> ColorMask” in
Pass Docs).

Performance Tips when Writing Shaders

Use Common sense ;)

Compute only things that you need; anything that is not actually needed can be eliminated. For example, supporting per-material color is nice to make a shader more flexible, but if you always leave that color set to white then it’s useless computations performed
for each vertex or pixel rendered on screen.

Another thing to keep in mind is frequency of computations. Usually there are many more pixels rendered (hence their pixel shaders executed) than there are vertices (vertex shader executions); and more vertices than objects being rendered. So generally if
you can, move computations out of pixel shader into the vertex shader; or out of shaders completely and set the values once from a script.

Less Generic Surface Shaders

Surface Shaders are great for writing shaders that interact with lighting. However, their default options are tuned for “general case”. In many cases, you can tweak them
to make shaders run faster or at least be smaller:

approxview
directive for shaders that use view direction (i.e. Specular) will make view direction be normalized per-vertex instead of per-pixel. This is approximate, but often good enough.
halfasview
for Specular shader types is even faster. Half-vector (halfway between lighting direction and view vector) will be computed and normalized per vertex, and

lighting function will already receive half-vector as a parameter instead of view vector.
noforwardadd
will make a shader fully support only one directional light in Forward rendering. The rest of the lights can still have an effect as per-vertex lights or spherical harmonics. This is great to make shader smaller and make sure it
always renders in one pass, even with multiple lights present.
noambient
will disable ambient lighting and spherical harmonics lights on a shader. This can be slightly faster.

Precision of computations

When writing shaders in Cg/HLSL, there are three basic number types:
float
,
half
and
fixed
(as well as vector/matrix variants of them, e.g. half3 and float4x4):

float
: high precision floating point. Generally 32 bits, just like float type in regular programming languages.
half
: medium precision floating point. Generally 16 bits, with a range of –60000 to +60000 and 3.3 decimal digits of precision.
fixed
: low precision fixed point. Generally 11 bits, with a range of –2.0 to +2.0 and 1/256th precision.

Use lowest precision that is possible; this is especially important on mobile platforms like iOS and Android. Good rules of thumb are:

For colors and unit length vectors, use
fixed
.
For others, use
half
if range and precision is fine; otherwise use
float
.

On mobile platforms, the key is to ensure as much as possible stays in low precision in the fragment shader. On most mobile GPUs, applying swizzles to low precision (fixed/lowp) types is costly; converting between fixed/lowp and higher precision types is
quite costly as well.

In practice, what exactly type will be used for float/half/fixed depends on the platform and the GPU. General summary:

All modern desktop GPUs will always compute everything in full floating point precision; float/half/fixed end up being exactly the same underneath. Which makes testing a bit tricky, since on PC it’s hard to see if half/fixed precision is really enough.
Make sure to test your shaders on actual mobile device!
Mobile GPUs have actual half-precision support, and it is often both faster and uses less power to do calculations.
“Fixed” precision is generally only useful for older mobile GPUs; most modern GPUs (the ones that can run OpenGL ES 3 or Metal) internally treat fixed and half exactly the same.

Alpha Testing

Fixed function
AlphaTest or it’s programmable equivalent,
clip()
, has different performance characteristics on different platforms:

Generally it’s a small advantage to use it to cull out totally transparent pixels on most platforms.
However, on PowerVR GPUs found in iOS and some Android devices, alpha testing is expensive. Do not try to use it as “performance optimization” there, it will be slower.

Color Mask

On some platforms (mostly mobile GPUs found in iOS and Android devices), using
ColorMask to leave out some channels (e.g.
ColorMask RGB
) can be expensive, so only use it if really necessary.

Unity’s Rendering Pipeline

Shaders define both how an object looks by itself (its material properties) and how it reacts to the light. Because lighting calculations must be built into the shader, and there are many possible light & shadow types, writing quality shaders that “just
work” would be an involved task. To make it easier, Unity has
Surface Shaders, where all the lighting, shadowing, lightmapping, forward vs. deferred rendering things are taken care of automatically.

This document describes the pecularities of Unity’s lighting & rendering pipeline and what happens behind the scenes of
Surface Shaders.

Rendering Paths

How lighting is applied and which
Passes of the shader are used depends on which
Rendering Path is used. Each pass in a shader communicates its lighting type via
Pass Tags.

In
Forward Rendering,
ForwardBase
and
ForwardAdd
passes are used.
In
Deferred Shading,
Deferred
pass is used.
In
legacy Deferred Lighting,
PrepassBase
and
PrepassFinal
passes are used.
In
legacy Vertex Lit,
Vertex
,
VertexLMRGBM
and
VertexLM
passes are used.
In any of the above, to render
Shadows or a depth texture,
ShadowCaster
pass is used.

Forward Rendering path

ForwardBase
pass renders ambient, lightmaps, main directional light and not important (vertex/SH) lights at once.
ForwardAdd
pass is used for any additive per-pixel lights; one invocation per object illuminated by such light is done. See

Forward Rendering for details.

If forward rendering is used, but a shader does not have forward-suitable passes (i.e. neither
ForwardBase
nor
ForwardAdd
pass types are present), then that object is rendered just like it would in Vertex Lit path, see below.

Deferred Shading path

Deferred
pass renders all information needed for lighting (in built-in shaders: diffuse color, specular color, smoothness,world space normal, emission). It also adds lightmaps, reflection probes and ambient lighting into the emission channel.
See
Deferred Shading for details.

Legacy Deferred Lighting path

PrepassBase
pass renders normals & specular exponent;
PrepassFinal
pass renders final color by combining textures, lighting & emissive material properties. All regular in-scene lighting is done separately in screen-space. See

Deferred Lighting for details.

Legacy Vertex Lit Rendering path

Since vertex lighting is most often used on platforms that do not support programmable shaders, Unity can’t create multiple shader variants internally to handle lightmapped vs. non-lightmapped cases. So to handle lightmapped and non-lightmapped objects,
multiple passes have to be written explicitly.

Vertex
pass is used for non-lightmapped objects. All lights are rendered at once, using a fixed function OpenGL/Direct3D lighting model (Blinn-Phong)
VertexLMRGBM
pass is used for lightmapped objects, when lightmaps are RGBM encoded (PC and consoles). No realtime lighting is applied; pass is expected to combine textures with a lightmap.
VertexLMM
pass is used for lightmapped objects, when lightmaps are double-LDR encoded (mobile platforms). No realtime lighting is applied; pass is expected to combine textures with a lightmap.

See Also

Graphics Command Buffers for how to extend Unity’s rendering pipeline.

Rendering Paths

Unity supports different Rendering Paths. You should choose which one you use depending on your game content and target platform / hardware. Different rendering paths have different performance characteristics that mostly affect Lights and Shadows.
See
render pipeline for technical details.

The rendering Path used by your project is chosen in
Player Settings. Additionally, you can override it for each
Camera.

If the graphics card can’t handle a selected rendering path, Unity will automatically use a lower fidelity one. For example, on a GPU that can’t handle Deferred Shading, Forward Rendering will be used.

Deferred Shading

Deferred Shading is the rendering path with the most lighting and shadow fidelity, and is best suited if you have many realtime lights. It requires a certain level of hardware support.

For more details see the
Deferred Shading page.

Forward Rendering

Forward is the traditional rendering path. It supports all the typical Unity graphics features (normal maps, per-pixel lights, shadows etc.). However under default settings, only a small number of the brightest lights are rendered in per-pixel lighting mode.
The rest of the lights are calculated at object vertices or per-object.

For more details see the
Forward Rendering page.

Legacy Deferred

Legacy Deferred (light prepass) is similar to Deferred Shading, just using a different technique with different trade-offs. It does not support the Unity 5 physically based standard shader.

For more details see the
Deferred Lighting page.

Legacy Vertex Lit

Legacy Vertex Lit is the rendering path with the lowest lighting fidelity and no support for realtime shadows. It is a subset of Forward rendering path.

For more details see the
Vertex Lit page.

Rendering Paths Comparison

[thead]
[/thead]

DeferredForwardLegacy DeferredVertex Lit
Features
Per-pixel lighting (normal maps, light cookies)YesYesYes-
Realtime shadowsYesWith caveatsYes-
Reflection ProbesYesYes--
Depth&Normals BuffersYesAdditional render passesYes-
Soft ParticlesYes-Yes-
Semitransparent objects-Yes-Yes
Anti-Aliasing-Yes-Yes
Light Culling MasksLimitedYesLimitedYes
Lighting FidelityAll per-pixelSome per-pixelAll per-pixelAll per-vertex
Performance
Cost of a per-pixel LightNumber of pixels it illuminatesNumber of pixels * Number of objects it illuminatesNumber of pixels it illuminates-
Number of times objects are normally rendered1Number of per-pixel lights21
Overhead for simple scenesHighNoneMediumNone
Platform Support
PC (Windows/Mac)Shader Model 3.0+ & MRTAllShader Model 3.0+All
Mobile (iOS/Android)OpenGL ES 3.0 & MRTAllOpenGL ES 2.0All
ConsolesXB1, PS4AllXB1, PS4, 360, PS3-
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: