Hi, I have a problem with implementing an outline shader (based on this tutorial: https://willweissman.wordpress.com/tutorials/shaders/unity-shaderlab-object-outlines/) in VR. What is basically does is render the scene to a texture, then render selected objects to another texture and outline those and at the end combine both textures. When running the game without an attached Vive, everything displays correctly: As you can see the block in the front is correctly outlined. When I run it in VR, two things happen: 1) When I look at the selected object I see the outline twice 2) If I look away from the object, I see the outline only once, but at the wrong position. This seems to be due to the shader being performed twice (maybe only on the screen space?) and therefore not correctly displaying the shader in VR. Can anyone clarify and explain how something like this can be solved? Here's my sample code: Script attached to the camera: Code (CSharp): using UnityEngine; using System.Collections; public class PostEffect : MonoBehaviour { Camera AttachedCamera; public Shader Post_Outline; public Shader DrawSimple; Camera TempCam; Material Post_Mat; void Start () { AttachedCamera = GetComponent<Camera>(); TempCam = new GameObject().AddComponent<Camera>(); TempCam.enabled = false; Post_Mat = new Material(Post_Outline); } void OnRenderImage(RenderTexture source, RenderTexture destination) { //set up a temporary camera TempCam.CopyFrom(AttachedCamera); TempCam.clearFlags = CameraClearFlags.Color; TempCam.backgroundColor = Color.black; //cull any layer that isn't the outline TempCam.cullingMask = 1 << LayerMask.NameToLayer("Outline"); //make the temporary rendertexture RenderTexture TempRT = new RenderTexture(source.width, source.height, 0, RenderTextureFormat.ARGB32); //put it to video memory TempRT.Create(); //set the camera's target texture when rendering TempCam.targetTexture = TempRT; //flip the camera, since VR image is somehow upside down TempCam.ResetWorldToCameraMatrix(); TempCam.ResetProjectionMatrix(); TempCam.projectionMatrix = TempCam.projectionMatrix * Matrix4x4.Scale(new Vector3(1, -1, 1)); //render all objects this camera can render, but with our custom shader. TempCam.RenderWithShader(DrawSimple,""); Graphics.Blit(source, destination); //copy the temporary RT to the final image Graphics.Blit(TempRT, destination, Post_Mat); //release the temporary RT TempRT.Release(); } } and this is the code that just renders the selected object in white to be outlined later. Here the effect is already visible: Code (CSharp): //This shader goes on the objects themselves. It just draws the object as white, and has the "Outline" tag. Shader "Custom/DrawSimple" { SubShader { ZWrite Off ZTest Always Lighting Off Pass { CGPROGRAM #pragma vertex VShader #pragma fragment FShader struct VertexToFragment { float4 pos:POSITION; }; //just get the position correct VertexToFragment VShader(VertexToFragment i) { VertexToFragment o; o.pos=UnityObjectToClipPos(i.pos); return o; } //return white fixed4 FShader():COLOR0 { return fixed4(1,1,1,1); } ENDCG } } } and lastly the post-processing that is used to outline the created white objects: Code (CSharp): Shader "Custom/Post Outline" { Properties { _MainTex("Main Texture",2D)="white"{} } SubShader { Blend SrcAlpha OneMinusSrcAlpha Pass { CGPROGRAM sampler2D _MainTex; //<SamplerName>_TexelSize is a float2 that says how much screen space a texel occupies. float2 _MainTex_TexelSize; #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct v2f { float4 pos : POSITION; float2 uvs : TEXCOORD0; }; v2f vert (appdata_base v) { v2f o; //Despite the fact that we are only drawing a quad to the screen, Unity requires us to multiply vertices by our MVP matrix, presumably to keep things working when inexperienced people try copying code from other shaders. o.pos = UnityObjectToClipPos(v.vertex); //Also, we need to fix the UVs to match our screen space coordinates. There is a Unity define for this that should normally be used. o.uvs = o.pos.xy / 2 + 0.5; return o; } half4 frag(v2f i) : COLOR { //arbitrary number of iterations for now int NumberOfIterations=5; //split texel size into smaller words float TX_x=_MainTex_TexelSize.x; float TX_y=_MainTex_TexelSize.y; //and a final intensity that increments based on surrounding intensities. float ColorIntensityInRadius; //if something already exists underneath the fragment, discard the fragment. if(tex2D(_MainTex,i.uvs.xy).r>0) { discard; } //for every iteration we need to do horizontally for(int k=0;k<NumberOfIterations;k+=1) { //for every iteration we need to do vertically for(int j=0;j<NumberOfIterations;j+=1) { //increase our output color by the pixels in the area ColorIntensityInRadius+=tex2D( _MainTex, i.uvs.xy+float2 ( (k-NumberOfIterations/2)*TX_x, (j-NumberOfIterations/2)*TX_y ) ).r; } } //output some intensity of teal return ColorIntensityInRadius*half4(0,1,1,1); } ENDCG } //end pass } //end subshader } //end shader
How would you expect this to look? In VR you've got two separate screen-spaces, from two different angles, so the outline won't be the same. EDIT: Generally, most screen-space effects don't really work properly in stereoscopic 3D.
I would expect it to look as it does in the screenshot in my post. However, in VR, if I look at the outlined object, the outline appears twice (once for left and right eye) with a slight offset from the object
Are you using any of the single pass or single pass instanced VR techniques? They need your shaders to have additional properties to function in this mode. Does the behaviour change if you do not touch the camera matrix? I've experienced that touching the matrix breaks the vr functionality of the camera.
In the XR settings I've chosen multipass. Is there anything that else that could be set wrong? Will check both of that later
Cool. Did my point about the problem of screen-space effects in 3D make sense? EDIT: I made a really pretty illustration in Paint: The red bars represent the outline of the box. In 2D it works fine because there's only one camera, and thus only one screen-space. In 3D there's two cameras and two screen-spaces from two different angles. Thus, the outline gets drawn from two different angles. This affects most screen-space effects.
Ok, I know found another, simpler solution: cakeslices very nice outline effect, that actually supports multipass stereoscopic rendering https://github.com/cakeslice/Outline-Effect
Hey, I found a way to make it work. On Unity 2018.2. The following changes are needed: 1) Don't flip the camera in your PostEffect.cs Code (CSharp): using UnityEngine; using System.Collections; public class PostEffect : MonoBehaviour { Camera AttachedCamera; public Shader Post_Outline; public Shader DrawSimple; Camera TempCam; Material Post_Mat; void Start() { AttachedCamera = GetComponent<Camera>(); Post_Outline = Shader.Find("Custom/Post Outline"); DrawSimple = Shader.Find("Custom/DrawSimple"); TempCam = new GameObject().AddComponent<Camera>(); //set up a temporary camera TempCam.CopyFrom(AttachedCamera); TempCam.clearFlags = CameraClearFlags.Color; TempCam.backgroundColor = Color.black; TempCam.renderingPath = RenderingPath.Forward; //cull any layer that isn't the outline TempCam.cullingMask = 1 << LayerMask.NameToLayer("Outline"); TempCam.gameObject.transform.parent = AttachedCamera.transform; TempCam.allowHDR = false; TempCam.enabled = false; Post_Mat = new Material(Post_Outline); } void OnRenderImage(RenderTexture source, RenderTexture destination) { //make the temporary rendertexture RenderTexture TempRT = new RenderTexture(source.width, source.height, 0, RenderTextureFormat.ARGB32); //put it to video memory TempRT.Create(); //set the camera's target texture when rendering TempCam.targetTexture = TempRT; //render all objects this camera can render, but with our custom shader. TempCam.RenderWithShader(DrawSimple, ""); Graphics.Blit(source, destination); //copy the temporary RT to the final image Graphics.Blit(TempRT, destination, Post_Mat); //release the temporary RT TempRT.Release(); } } 2. In the Post Outline shader frag function, use UnityStereoTransformScreenSpaceTex() function to apply the necessary transformation for each eye to the rendered texture. I modified the shader by using the #pragma vertex vert_img also, but the frag function is the important change. Code (CSharp): Shader "Custom/Post Outline" { Properties { _MainTex("Main Texture",2D)="white"{} } SubShader { Blend SrcAlpha OneMinusSrcAlpha Pass { CGPROGRAM #pragma vertex vert_img #pragma fragment frag #include "UnityCG.cginc" sampler2D _MainTex; half4 _MainTex_ST; //<SamplerName>_TexelSize is a float2 that says how much screen space a texel occupies. float2 _MainTex_TexelSize; half4 frag(v2f_img i) : SV_Target { //arbitrary number of iterations for now int NumberOfIterations=2; //split texel size into smaller words float TX_x=_MainTex_TexelSize.x; float TX_y=_MainTex_TexelSize.y; //and a final intensity that increments based on surrounding intensities. float ColorIntensityInRadius; fixed4 original = tex2D(_MainTex, UnityStereoTransformScreenSpaceTex(i.uv)); //if something already exists underneath the fragment, discard the fragment. if(original.r>0) { discard; } //for every iteration we need to do horizontally for(int k=0;k<NumberOfIterations;k+=1) { //for every iteration we need to do vertically for(int j=0;j<NumberOfIterations;j+=1) { float4 radiusTex = tex2D(_MainTex, UnityStereoTransformScreenSpaceTex(i.uv + float2 ( (k-NumberOfIterations/2)*TX_x, (j-NumberOfIterations/2)*TX_y ) )); //increase our output color by the pixels in the area ColorIntensityInRadius+=radiusTex; } } //output some intensity of teal return ColorIntensityInRadius*half4(0,1,1,1); } ENDCG } //end pass } //end subshader } //end shader Really hope this helps!
It's not letting me edit my comment, but to add to the above, it's using Single Pass stereo rendering..
Hola, tengo el mismo problema pero con este tutorial . cuando lo juego y tengo el oculus conectado, me muestra una imagen en cada ojo. ¿Alguna solución?
The Oculus pack contains a outline shader (it's in the distance grab example folder). The code will work with any VR headset. Im not sure if you are "allowed" to use the code for any other headsets, but im guess they will not care if you copy one of their example pack shaders, so you can just download the package and uncheck everything but the outline shader.