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.
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
}
}
}
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
}
}
}
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
}
}
}
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
}
}
}
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 (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:
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.
Vertical texture coordinate conventions differ between Direct3D-like and OpenGL-like platforms:
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.
To get shaders working on all platforms, some special shader values should use these semantics:
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.SV_Target
. Sometimes shaders use COLOR
or COLOR0
for that, but again that will not work on PS4.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.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 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.
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
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
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).
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.
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.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:
fixed
.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:
Fixed function AlphaTest or it’s programmable equivalent, clip()
, has different performance characteristics on different platforms:
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.
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.
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.
ForwardBase
and ForwardAdd
passes are used.Deferred
pass is used.PrepassBase
and PrepassFinal
passes are used.Vertex
, VertexLMRGBM
and VertexLM
passes are used.ShadowCaster
pass is used.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
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.
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.
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.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 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 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 (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 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.
Deferred | Forward | Legacy Deferred | Vertex Lit | |
---|---|---|---|---|
Features | ||||
Per-pixel lighting (normal maps, light cookies) | Yes | Yes | Yes | - |
Realtime shadows | Yes | With caveats | Yes | - |
Reflection Probes | Yes | Yes | - | - |
Depth&Normals Buffers | Yes | Additional render passes | Yes | - |
Soft Particles | Yes | - | Yes | - |
Semitransparent objects | - | Yes | - | Yes |
Anti-Aliasing | - | Yes | - | Yes |
Light Culling Masks | Limited | Yes | Limited | Yes |
Lighting Fidelity | All per-pixel | Some per-pixel | All per-pixel | All per-vertex |
Performance | ||||
Cost of a per-pixel Light | Number of pixels it illuminates | Number of pixels * Number of objects it illuminates | Number of pixels it illuminates | - |
Number of times objects are normally rendered | 1 | Number of per-pixel lights | 2 | 1 |
Overhead for simple scenes | High | None | Medium | None |
Platform Support | ||||
PC (Windows/Mac) | Shader Model 3.0+ & MRT | All | Shader Model 3.0+ | All |
Mobile (iOS/Android) | OpenGL ES 3.0 & MRT | All | OpenGL ES 2.0 | All |
Consoles | XB1, PS4 | All | XB1, PS4, 360, PS3 | - |