I’ve been using InvertCamera script from the wiki which scales the camera projection matrix y axis by -1 to flip it vertically. I’d hoped that I’d be able to tell that the projection matrix was flipped in a shader by looking at _ProjectionParams.x but this doesn’t seem to be the case… _ProjectionParams states that the x is 1.0 or -1.0 if the camera projection matrix is flipped… does that mean something different?
_ProjectionParams has no concept of what you do manually. Its .x is only -1 when you’re using Unity’s antialiasing image effect, as far as I know. The documentation is bad, and confused you, so you should report a bug about it.
What you should not do, is compare floating point numbers for equality. Your == 1.0 should have been > 0.
However, you shouldn’t be relying on conditionals in Unity 4.1 onwards. Use shader keywords nowadays for performance / battery life.
I tried x and various combinations for comparison (>0 etc). I realised that this just isn’t working to detect a manual tweak to the projection matrix via “InvertCamera” from the Unity Wiki.
Basically what I am attempting is something like this e.g. a Web 2.0 reflection but in 3d.
I don’t have IOS Pro, so can’t use rendertexture.
Idea #1
I got it working initially using that invertcamera script on a second camera and more or less got the effect I wanted, but needed to use a semitransparent plane to cover the reflection to make it look as though it were fading out. A 1/3rd of the screen transparent plane though isn’t great for IOS performance, especially when you’re covering this: :
Recently, though, I stumbled across the clipsafe shader on the wiki and managed to repurpose it to fade out the vertex colours as you go down the y axis, so in theory I can create the reflection bit purely in the shader.
The trouble is I then need it to work on the inverted camera, but not on the normal-way-up camera.
I managed to get it working using replacement shaders, but for some reason, when you use replacement shaders, things no longer dynamically batch. In my case, the game has 200 cubes, and took my draw calls from 20 ish to 250 (targetting IOS), so unacceptable.
The next thing I tried to do was to see if I could use _ProjectionParams to determine if the projection was inverted. If that were possible, I could rewrite the shader to only fade if inverted, and then just use the same shader for both cameras.
That didn’t work as per above.
My final hair-brained scheme today was to attempt to override / pass in the MVP matrix manually into the shader. That way I could just put two materials on the cubes, and the second material could have appropriate float4x4’s setup and populated via a SetGlobalMatrix, where I’d pass it the necessary constituents to make the equivalent of UNITY_MATRIX_MVP etc. This appeared to break batching too (and I couldn’t quite get it to work… niggly details, but again I think batching is the problem.)
Actually, I have one final idea, and that’s to use SetGlobalFloat or similar on the “OnPreCull” for each camera, setting it to 0 and 1 respectively, and then applying the fading only if the flag is set? Could that work?
I don’t understand why you’re not using two materials for every block. Just use the same shader for each material, do the modifications to whatever you want, based on shader keywords, and use a later queue for the materials that aren’t “reflected”.
I have tried multiple materials per block, but I haven’t been able to work out how to flip the camera projection in a shader, if that makes sense? InvertCamera applies to the entire camera. I don’t know how else to vertically flip it.
Then you’ll probably need to study how to work with matrices a bit. The math won’t be hard, but you’ll need to know where to fill in the correctly negated values. I like this page.
Basically, since the cameras render in order, and the OnPreCull … render happens in a sequence, I was able to use SetGlobalFloat on a shader parameter to effectively switch on and off the fading effect in the shader between camera renders.
The shader looks like this:
Shader "Particles/Fade alpha" {
Properties {
_MainTex ("Particle Texture", 2D) = "white" {}
}
SubShader {
Tags
{
"Queue" = "Transparent"
"RenderType"="Transparent"
}
Cull Back
Blend SrcAlpha OneMinusSrcAlpha
Lighting Off
BindChannels {
Bind "Color", color
Bind "Vertex", vertex
Bind "TexCoord", texcoord
}
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
uniform float4 _MainTex_ST,
_TintColor;
uniform float _FadeDistance;
uniform float4 _Origin;
// if > 0 then EffectOn, else not.
uniform float _EffectOn;
struct appdata_vert {
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
float4 color : COLOR;
};
uniform sampler2D _MainTex;
struct v2f {
float4 pos : SV_POSITION;
float2 uv;
float4 color;
};
v2f vert (appdata_vert v) {
v2f o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
float4 viewPos = mul(UNITY_MATRIX_MV, v.vertex);
//if _EffectOn = 1, then we get the appropriate alpha
//if _EffectOn = 0, then the (1-(1-a) * _EffectOn) == 1 aka no effect
//saturate to clamp the alpha between 0 1
float alpha = saturate((1 - ((-viewPos.y - _Origin.y) / _FadeDistance)) * _EffectOn);
o.color = float4(v.color.rgb, v.color.a * (1 - alpha));
return o;
}
float4 frag (v2f i) : COLOR {
half4 texcol = tex2D( _MainTex, i.uv );
return texcol*i.color;
}
ENDCG
}
}
}
And then on each camera I just have this in a c# script
public float EffectOn;
public Vector3 Origin;
public float FadeDistance;
void OnPreCull()
{
Shader.SetGlobalFloat("_EffectOn", EffectOn);
Shader.SetGlobalVector("_Origin",Origin);
Shader.SetGlobalFloat("_FadeDistance",FadeDistance);
}
With EffectOn set to 1 on the inverted camera. I probably don’t need to set the Origin / fade distance every frame, I guess.
Quite roundabout, but not too bad in the end, and at least meeting the requirement of 1 draw call per camera for all the vertex coloured cubes.