Hello,
for my open source RC helicopter game, where the pilot is standing at the constant position in the middle of a 360° skybox photo-image, and only the camera orientation changes, I used a custom shader to project the skybox photo-images onto the “ground object” (ground, trees, houses,…). Therefore a second camera is rendered into a “render texture” which is used in the shader: If the helicopter flies behind trees for examples, then it gets covered. The “ground object” catches also shadows.
Using the old non VR shader and deactivating XR leads to correct results:
I would like to change my existing shader to work in VR with the Single Pass Stereo rendering method. But due to lack of knowledge and experience I don’t get it work.
I followed the explanation in the documentation “Single Pass Instanced rendering” → Custom shaders (Unity - Manual: Single-pass instanced rendering and custom shaders)
Here I’m not sure, if my “GPU instancing” settings are right, I don’t want to have different parameters for the mesh instances. Therefore I only added “#pragma multi_compile_instancing” and activated it in the material settings.
Continuing in the doku I added the macros at the suggested positions:
UNITY_VERTEX_INPUT_INSTANCE_ID, UNITY_VERTEX_OUTPUT_STEREO, UNITY_SETUP_INSTANCE_ID(), UNITY_INITIALIZE_OUTPUT(v2f, o) UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO().
But the projection isn’t working (see image below) and also only the left eye shadow is at the correct position.
Could please give me somebody an advice, what I have to do to fix the shader?
Shader for non VR mode (working fine) :
// source:
// https://www.ronja-tutorials.com/2019/01/20/screenspace-texture.html
// https://discussions.unity.com/t/624555 ?_ga=2.69737753.803013357.1578766070-2133810121.1564613509#post-2606783
Shader "Custom/ground"
{
//show values to edit in inspector
Properties{
_Color("Tint", Color) = (0, 0, 0, 1)
_MainTex("Texture", 2D) = "white" {}
_ShadowStrength("Shadow Strength", Range(0, 1)) = 1
}
SubShader
{
//the material is completely non-transparent and is rendered at the same time as the other opaque geometry
Tags{ "RenderType" = "Opaque" "Queue" = "Geometry" }
Pass
{
CGPROGRAM
//include useful shader functions
#include "UnityCG.cginc"
//define vertex and fragment shader
#pragma vertex vert
#pragma fragment frag
//texture and transforms of the texture
sampler2D _MainTex;
float4 _MainTex_ST;
//tint of the texture
fixed4 _Color;
//the object data that's put into the vertex shader
struct appdata
{
float4 vertex : POSITION;
};
//the data that's used to generate fragments and can be read by the fragment shader
struct v2f
{
float4 position : SV_POSITION;
float4 screenPosition : TEXCOORD0;
};
//the vertex shader
v2f vert(appdata v)
{
v2f o;
//convert the vertex positions from object space to clip space so they can be rendered
o.position = UnityObjectToClipPos(v.vertex);
o.screenPosition = ComputeScreenPos(o.position);
return o;
}
//the fragment shader
fixed4 frag(v2f i) : SV_TARGET
{
fixed4 col = tex2Dproj(_MainTex, i.screenPosition);
col *= _Color;
return col;
}
ENDCG
}
Pass
{
Blend SrcAlpha OneMinusSrcAlpha
Tags{ "LightMode" = "ForwardBase" }
ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "AutoLight.cginc"
struct v2f
{
float4 pos : SV_POSITION;
SHADOW_COORDS(0)
};
fixed _ShadowStrength;
v2f vert(appdata_img v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag(v2f i) : COLOR
{
fixed shadow = SHADOW_ATTENUATION(i);
fixed shadowalpha = (1.0 - shadow) * _ShadowStrength;
return fixed4(0.0, 0.0, 0.0, shadowalpha);
}
ENDCG
}
Pass
{
Tags{ "LightMode" = "ShadowCaster" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#include "UnityCG.cginc"
struct v2f
{
V2F_SHADOW_CASTER;
};
v2f vert(appdata_base v)
{
v2f o;
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
return o;
}
float4 frag(v2f i) : SV_Target
{
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
//FallBack "Standard"
}
Shader edited for VR:
// source:
// https://www.ronja-tutorials.com/2019/01/20/screenspace-texture.html
// https://discussions.unity.com/t/624555 ?_ga=2.69737753.803013357.1578766070-2133810121.1564613509#post-2606783
Shader "Custom/ground2"
{
//show values to edit in inspector
Properties{
_Color("Tint", Color) = (0, 0, 0, 1)
_MainTex("Texture", 2D) = "white" {}
_ShadowStrength("Shadow Strength", Range(0, 1)) = 1
}
SubShader
{
//the material is completely non-transparent and is rendered at the same time as the other opaque geometry
Tags{ "RenderType" = "Opaque" "Queue" = "Geometry" }
Pass
{
CGPROGRAM
//include useful shader functions
#include "UnityCG.cginc"
//define vertex and fragment shader
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_instancing // ##### ADDED #####
//texture and transforms of the texture
sampler2D _MainTex;
float4 _MainTex_ST;
//tint of the texture
fixed4 _Color;
//the object data that's put into the vertex shader
struct appdata
{
float4 vertex : POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID // ##### ADDED #####
};
//the data that's used to generate fragments and can be read by the fragment shader
struct v2f
{
float4 position : SV_POSITION;
float4 screenPosition : TEXCOORD0;
UNITY_VERTEX_OUTPUT_STEREO // ##### ADDED #####
};
//the vertex shader
v2f vert(appdata v)
{
v2f o;
// UNITY_SETUP_INSTANCE_ID() calculates and sets the built - in unity_StereoEyeIndex and unity_InstanceID Unity shader variables
// to the correct values based on which eye the GPU is currently rendering.
// It must be used at the very beginning of a vertex Shader
UNITY_SETUP_INSTANCE_ID(v); // ##### ADDED #####
// initializes all v2f values to 0
UNITY_INITIALIZE_OUTPUT(v2f, o); // ##### ADDED #####
// UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO tells the GPU which eye in the texture array it should render to,
// based on the value of unity_StereoEyeIndex.This macro also transfers the value of unity_StereoEyeIndex from the vertex shader
// so that it will be accessible in the fragment shader only if UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX is called in the fragment shader frag method.
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); // ##### ADDED #####
// necessary only if you want to access instanced properties in the fragment Shader.
//UNITY_TRANSFER_INSTANCE_ID(v, o);
//convert the vertex positions from object space to clip space so they can be rendered
o.position = UnityObjectToClipPos(v.vertex);
o.screenPosition = ComputeScreenPos(o.position); // ComputeNonStereoScreenPos(o.position);
return o;
}
//the fragment shader
fixed4 frag(v2f i) : SV_TARGET
{
fixed4 col = tex2Dproj(_MainTex, i.screenPosition);
col *= _Color;
return col;
}
ENDCG
}
Pass
{
Blend SrcAlpha OneMinusSrcAlpha
Tags{ "LightMode" = "ForwardBase" }
ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "AutoLight.cginc"
struct v2f
{
float4 pos : SV_POSITION;
SHADOW_COORDS(0)
UNITY_VERTEX_OUTPUT_STEREO // ##### ADDED #####
};
fixed _ShadowStrength;
v2f vert(appdata_img v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v); // ##### ADDED #####
UNITY_INITIALIZE_OUTPUT(v2f, o); // ##### ADDED #####
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); // ##### ADDED #####
o.pos = UnityObjectToClipPos(v.vertex);
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag(v2f i) : COLOR
{
fixed shadow = SHADOW_ATTENUATION(i);
fixed shadowalpha = (1.0 - shadow) * _ShadowStrength;
return fixed4(0.0, 0.0, 0.0, shadowalpha);
}
ENDCG
}
Pass
{
Tags{ "LightMode" = "ShadowCaster" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#include "UnityCG.cginc"
struct v2f
{
V2F_SHADOW_CASTER;
UNITY_VERTEX_OUTPUT_STEREO // ##### ADDED #####
};
v2f vert(appdata_base v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v); // ##### ADDED #####
UNITY_INITIALIZE_OUTPUT(v2f, o); // ##### ADDED #####
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); // ##### ADDED #####
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
return o;
}
float4 frag(v2f i) : SV_Target
{
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
//FallBack "Standard"
}
Setup:
Win10 / Unity 2020.2.a13 / OpenVR XR Plugin Version 1.0.0-preview.2 / HTC Vive Cosmos, Per Eye 1440 x 1700
Second Camera (as child of Main Camera under XRig):
Culling Mask → nothing / Field of View → 93.59882 (same as Main Camera) / Depth → 0 / Target Eye -->None (Main Display)
Project Settings → XR Plug-in Management → OpenVR:
App Type: Scene / Stereo Rend. Mode: Single Pass Instanced / Mirror View Mode: None