Using Unity 2017.3
I have a RawImage into which I display a sequence of images loaded as Texture2D's. Works perfectly and seamlessly.
I then show a video into the same RawImage, using the sample VideoPlayer code, and assigning
rawImage.texture = videoPlayer.texture;
The video plays perfectly well, but as part of switching from the still images to the video, there is a noticeable flicker in the RawImage, as if there's a frame or two of black displayed. The first frame of the video matches the last static image I displayed, so I had expected the transition to be pretty seamless.
Note that the video has been "Prepared" prior to all this - my code yields until videoPlayer.isPrepared returns true, and only then tell the video to play and set the texture.
I thought maybe there was an issue with the texture not being quite ready, but I tried yielding once or twice after calling Play and before setting the texture, but that didn't have any effect on the flicker.
I saw this item: https://answers.unity.com/questions/1294011/raw-image-flickering-when-texture-changed.html which suggests that this is something to do with material instances being set up. I don't fully understand the solution presented in that answer, nor do I understand how I could adapt it to my own case, but maybe it means something to those more skilled in Unity than I.
Any suggestions on how to get rid of that flickery frame?
EDIT: Here's the code
public class VideoAnimation : MonoBehaviour, IAnimation {
private VideoPlayer _videoPlayer;
private UnityAction _completeAction;
private bool _prepareStarted;
public void Configure(VideoClip clip, bool looping)
{
_videoPlayer = gameObject.AddComponent<VideoPlayer>();
_videoPlayer.playOnAwake = false;
_videoPlayer.isLooping = looping;
_videoPlayer.source = VideoSource.VideoClip;
_videoPlayer.clip = clip;
_videoPlayer.skipOnDrop = true;
}
public void Prepare()
{
_prepareStarted = true;
_videoPlayer.Prepare();
}
public void Begin(RawImage destImage, UnityAction completeAction)
{
_completeAction = completeAction;
_videoPlayer.loopPointReached += OnLoopPointReached;
StartCoroutine(GetVideoPlaying(destImage));
}
private IEnumerator GetVideoPlaying(RawImage destImage)
{
if (!_prepareStarted)
{
_videoPlayer.Prepare();
}
while(!_videoPlayer.isPrepared)
{
yield return null;
}
_videoPlayer.Play();
destImage.texture = _videoPlayer.texture;
}
public void OnLoopPointReached(VideoPlayer source)
{
if (_completeAction != null)
{
_completeAction();
}
}
public void End()
{
_videoPlayer.Stop();
_videoPlayer.loopPointReached -= OnLoopPointReached;
}
public class Factory : Factory<VideoAnimation>
{
}
}
In the specific case I'm dealing with, Configure and Prepare are called ahead of time, while the RawImage is showing the last static image before the video. Then when it's time to show the video, Begin is called. Thus, _prepareStarted is already true when Begin is called. Inserting log messages shows that isPrepared is returning true by the time I get around to calling Begin, so I don't loop there either.
I've tried altering the order of the two line
_videoPlayer.Play();
destImage.texture = _videoPlayer.texture;
but it doesn't seem to change anything. I also thought that maybe the VideoPlayer was somehow outputting a black frame ahead of the normal video, but inserting a yield or three after Play and before the texture set made no difference.
None of the samples I've seen have a Texture in the RawImage before the VideoPlayer's texture is inserted. So in those, the RawImage is starting out black, which means that an extra black frame isn't going to be noticeable.
EDIT #2:
Well, I came up with a solution and, I think, somewhat of an explanation.
First, VideoPlayer.frame is documented as "The frame index currently being displayed by the VideoPlayer." This is not strictly true. Or, maybe it is somewhere in the VideoPlayer's pipeline, but it's not the frame that's observable by code using the VideoPlayer's texture.
When you Prepare the VideoPlayer, at least in the mode I'm using it, the VideoPlayer creates an internal RenderTexture. You would think that, once the player has been prepared, that texture would contain the first frame of the video. It doesn't. There is a very noticeable delay before there's anything there. Thus, when my code set the RawImage texture to the player's texture, it was arranging for a texture that was, at least at that moment, empty to be displayed. This perfectly explains the black flicker, since that's the color of the background Canvas.
So my first attempt at a solution was to insert the loop here:
_videoPlayer.Play();
while(_videoPlayer.frame < 1)
{
yield return null;
}
destImage.texture = _videoPlayer.texture;
between Play and the texture set.
I figured that, despite the documentation, maybe frame was the frame about to be displayed. If so, this should result in the first (0th) frame already being in the buffer, and would get rid of the flicker. Nope. Still flickered. But when I changed to
_videoPlayer.Play();
while(_videoPlayer.frame < 2)
{
yield return null;
}
destImage.texture = _videoPlayer.texture;
then the transition was seamless. So my initial attempt where I inserted yields between the two was the right approach - I just didn't insert quite enough. One short, as a matter of fact. I inserted a counter in the loop, and it showed that I yielded 4 times in the above loop, which is what I would expect, since the video is 30fps, and I'm running at 60fps on my computer. (Sync lock is on.)
A final experiment showed that:
_videoPlayer.Play();
while(_videoPlayer.frame < 1)
{
yield return null;
}
yield return null;
destImage.texture = _videoPlayer.texture;
also did not result in a flicker. (Or, at least, not one that I could see.) So once the VideoPlayer was reporting that it was displaying the second frame (the numbers are 0-based according to the docs), it took one additional game frame before the transition was seamless. (Unless there was a 60-th of a second flicker that my eyes can't see.) That game frame might have something to do with Unity's graphic pipeline or VideoPlayer pipeline - I don't know.
So, the bottom line is that there is a noticeable delay from the time you call Play until there is actually anything in the VideoPlayer's texture that will make it to the screen, and unless you wait for that, you'll be displaying "nothing" (which, in my case, resulted in black background flickering through.)
It occurs to me that since the VideoPlayer is producing a RenderTexture, it might also be possible to blit the previous static texture to the VideoPlayer's texture (so that there would be something there right away) and then do the switch immediately. Another experiment to run...
Hmm, let's try use shaders, maybe it helps you.
First we must create custom shader and it must work like a standard UI shader.
You can download all build-in shaders in this link.
Take UI-Default.shader and modificate it. I modificate it for you!
Just create shader in unity and paste this code:
Shader "Custom/CustomShaderForUI"
{
Properties
{
//[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_CustomTexture ("Texture", 2D) = "white" {} // <--------------- new property
_Color ("Tint", Color) = (1,1,1,1)
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
_ColorMask ("Color Mask", Float) = 15
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
Cull Off
Lighting Off
ZWrite Off
ZTest [unity_GUIZTestMode]
Blend SrcAlpha OneMinusSrcAlpha
ColorMask [_ColorMask]
Pass
{
Name "Default"
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#pragma multi_compile __ UNITY_UI_CLIP_RECT
#pragma multi_compile __ UNITY_UI_ALPHACLIP
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
float4 worldPosition : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
fixed4 _Color;
fixed4 _TextureSampleAdd;
float4 _ClipRect;
v2f vert(appdata_t v)
{
v2f OUT;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
OUT.worldPosition = v.vertex;
OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
OUT.texcoord = v.texcoord;
OUT.color = v.color * _Color;
return OUT;
}
//sampler2D _MainTex;
sampler2D _CustomTexture; // <---------------------- new property
fixed4 frag(v2f IN) : SV_Target
{
//half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
half4 color = (tex2D(_CustomTexture, IN.texcoord) + _TextureSampleAdd) * IN.color; // <- using new property
#ifdef UNITY_UI_CLIP_RECT
color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
#endif
#ifdef UNITY_UI_ALPHACLIP
clip (color.a - 0.001);
#endif
return color;
}
ENDCG
}
}
}
Next, create a material and set your RenderTexture to texture field in shader (not in RawImage component).
Hope this helps!
Try hiding the RawImage gameObject while the video is loading. This should fix any flickering caused by the VideoPlayer not being fully loaded.
public VideoPlayer _videoPlayer;
public RawImage _videoImage;
private void PlayClip(VideoClip videoClip)
{
StartCoroutine(PlayClipCoroutine(videoClip));
}
private IEnumerator PlayClipCoroutine(VideoClip clip)
{
_videoImage.gameObject.SetActive(false);
_videoPlayer.clip = clip;
_videoPlayer.Prepare();
while (!_videoPlayer.isPrepared)
{
yield return null;
}
_videoPlayer.Play();
_videoImage.texture = _videoPlayer.texture;
_videoImage.gameObject.SetActive(true);
}
Related
In Unity3D I try to render a creature and display an outline when it is selected.
The creature alone is rendered fine:
I downloaded an Outline Shader on Github and applied it as a second material to my mesh:
With the expanded materials looking like this:
However, the result is not at all as expected:
Without knowing much about materials and shaders, I tried fiddling around and found out that if I change the Rendering Mode of the Standard material to transparent the result looks fine:
But now the creature alone renders in a kind of strange way where the limbs are overlapping the body:
What is the correct way to achieve what I'm trying to do? Do you have resources where I can read more?
The problem with your setup is the Render Queue. Transparent objects are rendered after opaque ones so your outline just draws on top of the creature. If you want to change the rendering order you have to treat the object with an outline as a "special" opaque object (eg. draw normal objects, draw outline, draw creature).
Here are a couple of alternatives:
Use Cull Front - This shader is basically drawing a bigger copy of the object on top of the original, like a shell. Cull Front makes it so it draws the back, instead of the front, of the shell which is behind the object.
Use the stencil buffer to mark the region where the original object is drawn and skip it when you draw the outline.
Below is a modified version of your shader (removed second color pass and surface shader pass since you don't use them). This is the stencil buffer option. If you want to try the other one, remove the first pass, the stencil block in the second pass and replace Cull Back with Cull Front.
Shader "Outlined/UltimateOutline"
{
Properties
{
_Color("Main Color", Color) = (0.5,0.5,0.5,1)
_FirstOutlineColor("Outline color", Color) = (1,0,0,0.5)
_FirstOutlineWidth("Outlines width", Range(0.0, 2.0)) = 0.15
_Angle("Switch shader on angle", Range(0.0, 180.0)) = 89
}
CGINCLUDE
#include "UnityCG.cginc"
struct appdata {
float4 vertex : POSITION;
float4 normal : NORMAL;
};
uniform float4 _FirstOutlineColor;
uniform float _FirstOutlineWidth;
uniform float4 _Color;
uniform float _Angle;
ENDCG
SubShader{
Pass {
Tags{ "Queue" = "Transparent-1" "IgnoreProjector" = "True" }
ZWrite Off
Stencil {
Ref 1
Comp always
Pass replace
}
ColorMask 0
}
//First outline
Pass{
Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }
Stencil {
Ref 1
Comp NotEqual
}
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
Cull Back //Replace this with Cull Front for option 1
CGPROGRAM
struct v2f {
float4 pos : SV_POSITION;
};
#pragma vertex vert
#pragma fragment frag
v2f vert(appdata v) {
appdata original = v;
float3 scaleDir = normalize(v.vertex.xyz - float4(0,0,0,1));
//This shader consists of 2 ways of generating outline that are dynamically switched based on demiliter angle
//If vertex normal is pointed away from object origin then custom outline generation is used (based on scaling along the origin-vertex vector)
//Otherwise the old-school normal vector scaling is used
//This way prevents weird artifacts from being created when using either of the methods
if (degrees(acos(dot(scaleDir.xyz, v.normal.xyz))) > _Angle) {
v.vertex.xyz += normalize(v.normal.xyz) * _FirstOutlineWidth;
}
else {
v.vertex.xyz += scaleDir * _FirstOutlineWidth;
}
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
return o;
}
half4 frag(v2f i) : COLOR{
return _FirstOutlineColor;
}
ENDCG
}
}
Fallback "Diffuse"
}
I am developing a native (c++) plugin (windows only for now) for unity (2018.1.0f2).
The plugin downloads textures and meshes and provides them to the unity.
There is a LOT of boilerplate code that I would like to spare you of.
Anyway, the rendering is done like this:
void RegenerateCommandBuffer(CommandBuffer buffer, List<DrawTask> tasks)
{
buffer.Clear();
buffer.SetProjectionMatrix(cam.projectionMatrix); // protected Camera cam; cam = GetComponent<Camera>();
foreach (DrawTask t in tasks)
{
if (t.mesh == null)
continue;
MaterialPropertyBlock mat = new MaterialPropertyBlock();
bool monochromatic = false;
if (t.texColor != null)
{
var tt = t.texColor as VtsTexture;
mat.SetTexture(shaderPropertyMainTex, tt.Get());
monochromatic = tt.monochromatic;
}
if (t.texMask != null)
{
var tt = t.texMask as VtsTexture;
mat.SetTexture(shaderPropertyMaskTex, tt.Get());
}
mat.SetMatrix(shaderPropertyUvMat, VtsUtil.V2U33(t.data.uvm));
mat.SetVector(shaderPropertyUvClip, VtsUtil.V2U4(t.data.uvClip));
mat.SetVector(shaderPropertyColor, VtsUtil.V2U4(t.data.color));
// flags: mask, monochromatic, flat shading, uv source
mat.SetVector(shaderPropertyFlags, new Vector4(t.texMask == null ? 0 : 1, monochromatic ? 1 : 0, 0, t.data.externalUv ? 1 : 0));
buffer.DrawMesh((t.mesh as VtsMesh).Get(), VtsUtil.V2U44(t.data.mv), material, 0, -1, mat);
}
}
There are two control modes. Either the unity camera is controlled by the camera in the plugin, or the plugin camera is controlled by the unity camera. In my current scenario, the plugin camera is controlled by the unity camera. There is no special magic behind the scenes, but some of the transformations needs to be done in double precision to work without meshes 'jumping' around.
void CamOverrideView(ref double[] values)
{
Matrix4x4 Mu = mapTrans.localToWorldMatrix * VtsUtil.UnityToVtsMatrix;
// view matrix
if (controlTransformation == VtsDataControl.Vts)
cam.worldToCameraMatrix = VtsUtil.V2U44(Math.Mul44x44(values, Math.Inverse44(VtsUtil.U2V44(Mu))));
else
values = Math.Mul44x44(VtsUtil.U2V44(cam.worldToCameraMatrix), VtsUtil.U2V44(Mu));
}
void CamOverrideParameters(ref double fov, ref double aspect, ref double near, ref double far)
{
// fov
if (controlFov == VtsDataControl.Vts)
cam.fieldOfView = (float)fov;
else
fov = cam.fieldOfView;
// near & far
if (controlNearFar == VtsDataControl.Vts)
{
cam.nearClipPlane = (float)near;
cam.farClipPlane = (float)far;
}
else
{
near = cam.nearClipPlane;
far = cam.farClipPlane;
}
}
And a shader:
Shader "Vts/UnlitShader"
{
SubShader
{
Tags { "RenderType" = "Opaque" }
LOD 100
Pass
{
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct vIn
{
float4 vertex : POSITION;
float2 uvInternal : TEXCOORD0;
float2 uvExternal : TEXCOORD1;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 uvTex : TEXCOORD0;
float2 uvClip : TEXCOORD1;
};
struct fOut
{
float4 color : SV_Target;
};
sampler2D _MainTex;
sampler2D _MaskTex;
float4x4 _UvMat;
float4 _UvClip;
float4 _Color;
float4 _Flags; // mask, monochromatic, flat shading, uv source
v2f vert (vIn i)
{
v2f o;
o.vertex = UnityObjectToClipPos(i.vertex);
o.uvTex = mul((float3x3)_UvMat, float3(_Flags.w > 0 ? i.uvExternal : i.uvInternal, 1.0)).xy;
o.uvClip = i.uvExternal;
return o;
}
fOut frag (v2f i)
{
fOut o;
// texture color
o.color = tex2D(_MainTex, i.uvTex);
if (_Flags.y > 0)
o.color = o.color.rrra; // monochromatic texture
// uv clipping
if ( i.uvClip.x < _UvClip.x
|| i.uvClip.y < _UvClip.y
|| i.uvClip.x > _UvClip.z
|| i.uvClip.y > _UvClip.w)
discard;
// mask
if (_Flags.x > 0)
{
if (tex2D(_MaskTex, i.uvTex).r < 0.5)
discard;
}
// uniform tint
o.color *= _Color;
return o;
}
ENDCG
}
}
}
It all works perfectly - in editor. It also works well in standalone DEVELOPMENT build. But the transformations get wrong when in 'deploy' builds. The rendered parts look as if they were rotated around wrong axes or with different polarities.
Can you spot some obvious mistakes?
My first suspect was OpenGL vs DirectX differences, but the 'deploy' and 'development' builds should use the same, should they not? Moreover, I have tried changing the player setting to force one or the other, but without any differences.
Edit:
Good image: https://drive.google.com/open?id=1RTlVZBSAj7LIml1sBCX7nYTvMNaN0xK-
Bad image: https://drive.google.com/open?id=176ahft7En6MqT-aS2RdKXOVW68NmvK2L
Note how the terrain is correctly aligned with the atmosphere.
Steps to reproduce
1) Create a new project in unity
2) Download the assets https://drive.google.com/open?id=18uKuiya5XycjGWEcsF-xjy0fn7sf-D82 and extract them into the newly created project
3) Try it in editor -> should work ok (it will start downloading meshes and textures from us, so be patient; the downloaded resources are cached in eg. C://users//.cache/vts-browser)
The plane is controlled by mouse with LMB pressed.
4) Build in development build and run -> should work ok too
5) Build NOT in development build and run -> the terrain transformations behave incorrectly.
Furthermore, I have published the repository. Here is the unity-specific code: https://github.com/Melown/vts-browser-unity-plugin
Unfortunately, I did not intend to publish it this soon, so the repository is missing some formal things like readme and build instructions. Most information can, however, be found in the submodules.
CommandBuffer.SetProjectionMatrix apparently needs a matrix that has been adjusted by GL.GetGPUProjectionMatrix.
buffer.SetProjectionMatrix(GL.GetGPUProjectionMatrix(cam.projectionMatrix, false));
Unfortunately, I still do not understand why would this cause a different behavior between deploy and development builds. I would have expected it to only make difference on different platforms.
I am looking for a glass shader for Unity that only refracts the objects behind it, or ideas for how to modify an existing glass shader to do that.
This screenshot shows what happens when I use FX/Glass/Stained BumpDistort on a curved plane mesh.
As you can see, the glass shader refracts both the sphere in front of the mesh and the ground behind it. I am looking for a shader that will only refract the objects behind it.
Here is the code for that shader, for reference:
// Per pixel bumped refraction.
// Uses a normal map to distort the image behind, and
// an additional texture to tint the color.
Shader "FX/Glass/Stained BumpDistort" {
Properties {
_BumpAmt ("Distortion", range (0,128)) = 10
_MainTex ("Tint Color (RGB)", 2D) = "white" {}
_BumpMap ("Normalmap", 2D) = "bump" {}
}
Category {
// We must be transparent, so other objects are drawn before this one.
Tags { "Queue"="Transparent" "RenderType"="Opaque" }
SubShader {
// This pass grabs the screen behind the object into a texture.
// We can access the result in the next pass as _GrabTexture
GrabPass {
Name "BASE"
Tags { "LightMode" = "Always" }
}
// Main pass: Take the texture grabbed above and use the bumpmap to perturb it
// on to the screen
Pass {
Name "BASE"
Tags { "LightMode" = "Always" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata_t {
float4 vertex : POSITION;
float2 texcoord: TEXCOORD0;
};
struct v2f {
float4 vertex : SV_POSITION;
float4 uvgrab : TEXCOORD0;
float2 uvbump : TEXCOORD1;
float2 uvmain : TEXCOORD2;
UNITY_FOG_COORDS(3)
};
float _BumpAmt;
float4 _BumpMap_ST;
float4 _MainTex_ST;
v2f vert (appdata_t v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
#if UNITY_UV_STARTS_AT_TOP
float scale = -1.0;
#else
float scale = 1.0;
#endif
o.uvgrab.xy = (float2(o.vertex.x, o.vertex.y*scale) + o.vertex.w) * 0.5;
o.uvgrab.zw = o.vertex.zw;
o.uvbump = TRANSFORM_TEX( v.texcoord, _BumpMap );
o.uvmain = TRANSFORM_TEX( v.texcoord, _MainTex );
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
sampler2D _GrabTexture;
float4 _GrabTexture_TexelSize;
sampler2D _BumpMap;
sampler2D _MainTex;
half4 frag (v2f i) : SV_Target
{
// calculate perturbed coordinates
half2 bump = UnpackNormal(tex2D( _BumpMap, i.uvbump )).rg; // we could optimize this by just reading the x & y without reconstructing the Z
float2 offset = bump * _BumpAmt * _GrabTexture_TexelSize.xy;
i.uvgrab.xy = offset * i.uvgrab.z + i.uvgrab.xy;
half4 col = tex2Dproj( _GrabTexture, UNITY_PROJ_COORD(i.uvgrab));
half4 tint = tex2D(_MainTex, i.uvmain);
col *= tint;
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
// ------------------------------------------------------------------
// Fallback for older cards and Unity non-Pro
SubShader {
Blend DstColor Zero
Pass {
Name "BASE"
SetTexture [_MainTex] { combine texture }
}
}
}
}
My intuition is that it has to do with the way that _GrabTexture is captured, but I'm not entirely sure. I'd appreciate any advice. Thanks!
No simple answer for this.
You cannot think about refraction without thinking about the context in some way, so let's see:
Basically, it's not easy to define when an object is "behind" another one. There are different ways to even meassure a point's distance to the camera, let alone accounting for the whole geometry. There are many strange situations where geometry intersects, and the centers and bounds could be anywhere.
Refraction is usually easy to think about in raytracing algorithms (you just march a ray and calculate how it bounces/refracts to get the colors). But here in raster graphics (used for 99% of real-time graphics), the objects are rendered as a whole, and in turns.
What is going on with that image is that the background and ball are rendered first, and the glass later. The glass doesn't "refract" anything, it just draws itself as a distortion of whatever was written in the render buffer before.
"Before" is key here. You don't get "behinds" in raster graphics, everything is done by being conscious of rendering order. Let's see how some refractions are created:
Manually set render queue tags for the shaders, so you know at what point in the pipeline they are drawn
Manually set each material's render queue
Create a script that constantly marshals the scene and every frame calculates what should be drawn before or after the glass according to position or any method you want, and set up the render queues in the materials
Create a script that render the scene filtering out (through various methods) the objects that shouldn't be refracted, and use that as the texture to refract (depending on the complexity of the scene, this is sometimes necessary)
These are just some options off the top of my head, everything depends on your scene
My advice:
Select the ball's material
Right-click on the Inspector window --> Tick on "Debug" mode
Set the Custom Render Queue to 2200 (after the regular geometry is drawn)
Select the glass' material
Set the Custom Render Queue to 2100 (after most geometry, but before the ball)
I want to draw view of camera in texture and show texture in canvas, if camera not moving. I run it for android then I got black texuture, but for WebPlayer is Good!
public RawImage rawImage;
private void Start()
{
texture = new RenderTexture(camera.pixelWidth, camera.pixelHeight, 32, RenderTextureFormat.ARGB32);
texture.antiAliasing = 8;
texture.Create();
}
public void ShowTexture()
{
camera.targetTexture = texture;
RenderTexture.active = texture2;
if (!RenderTexture.active.IsCreated())
RenderTexture.active.Create();
camera.Render();
var texture2d = new Texture2D(camera.targetTexture.width, camera.targetTexture.height, TextureFormat.RGB24, true, true);
texture2d.ReadPixels(new Rect(0, 0, camera.pixelWidth, camera.pixelHeight), 0, 0);
texture2d.Apply(false);
RenderTexture.active = null;
camera.targetTexture = null;
rawImage.texture = texture2d;
}
figure this might help someone...
Replicated same issue by accident... working both on iOS and Droid.
Then disabled camera clear, and hey presto... works on iOS but black on Android.
changed camera clear back to Z-depth only... and both iOS and Droid work again.
So try changing your camera clear settings.
I am currently working with unity ver 2019.2.0f1.
For unknown reasons the initial camera I had imported from an earlier version was showing a black render texture and the new one was working perfectly.
I seem to have found a solution, something on the camera itself seems to be the issue. Delete camera, re-create and Color Format on the render texture rgba8_unorm with depth buffer at 24
Now both show up properly on android.
Hope this helps
This is a known issue with Unity. You can find more details at:
https://forum.unity3d.com/threads/rendertexture-not-working-on-ios-and-android-unity-4-2-0f4.192561/
https://forum.unity3d.com/threads/render-texture-not-working-on-device-unity-5-2-1f1.358483/
https://forum.unity3d.com/threads/render-texture-works-in-editor-but-not-on-devices-after-upgrade-to-unity-5.362397/
and a few others where the moderators and staff claim it is fixed in a future release or (with a touch of unnecessary arrogance) that the issue is the user and a bug never existed at all.
BUT
This is going to sound silly, but add an ImageEffect to the main camera. I have made a dummy effect that is attached to my main camera and without any logical explanation, it fixes RenderTexture on mobile.
DummyEffect.cs:
using UnityEngine;
[ExecuteInEditMode]
[AddComponentMenu("Image Effects/Dummy Effect")]
public class DummyEffect : ImageEffectBase {
// Called by camera to apply image effect
void OnRenderImage (RenderTexture source, RenderTexture destination) {
Graphics.Blit (source, destination, material);
}
}
DummyEffect.shader:
Shader "Hidden/Dummy Effect" {
Properties {
_MainTex ("Base (RGB)", RECT) = "white" {}
}
SubShader {
Pass {
ZTest Always Cull Off ZWrite Off
Fog { Mode off }
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
uniform sampler2D _MainTex;
float4 frag (v2f_img i) : COLOR {
return tex2D(_MainTex, i.uv);
}
ENDCG
}
}
Fallback off
}
I've written a shader and it works fine when I added it in a plane located in front of camera (in this case camera does not have shader). but then I add this shader to the camera, it does not show anything on the screen. Herein is my code, could you let me know how can I change it to be compatible with Camera.RenderWithShader method?
Shader "Custom/she1" {
Properties {
top("Top", Range(0,2)) = 1
bottom("Bottom", Range(0,2)) = 1
}
SubShader {
// Draw ourselves after all opaque geometry
Tags { "Queue" = "Transparent" }
// Grab the screen behind the object into _GrabTexture
GrabPass { }
// Render the object with the texture generated above
Pass {
CGPROGRAM
#pragma debug
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
sampler2D _GrabTexture : register(s0);
float top;
float bottom;
struct data {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f {
float4 position : POSITION;
float4 screenPos : TEXCOORD0;
};
v2f vert(data i){
v2f o;
o.position = mul(UNITY_MATRIX_MVP, i.vertex);
o.screenPos = o.position;
return o;
}
half4 frag( v2f i ) : COLOR
{
float2 screenPos = i.screenPos.xy / i.screenPos.w;
float _half = (top + bottom) * 0.5;
float _diff = (bottom - top) * 0.5;
screenPos.x = screenPos.x * (_half + _diff * screenPos.y);
screenPos.x = (screenPos.x + 1) * 0.5;
screenPos.y = 1-(screenPos.y + 1) * 0.5 ;
half4 sum = half4(0.0h,0.0h,0.0h,0.0h);
sum = tex2D( _GrabTexture, screenPos);
return sum;
}
ENDCG
}
}
Fallback Off
}
I think what your asking for is a replacement shader that shades everything in the camera with your shader.
Am I correct?
If so this should work
Camera.main.SetReplacementShader(Shader.Find("Your Shader"),"RenderType")
here is some more info:
http://docs.unity3d.com/Documentation/Components/SL-ShaderReplacement.html
Edit: Are you expecting the entire camera to warp like a lens effect? Because your not going to get that using a shader like this by itself, because as it stands it will only apply to objects like your plane but not the full camera view, that requires a post image effect. First your need Unity Pro. If you do, import the Image effects package and look at the fisheye script. See if you can duplicate the fisheye script with your own shader. When I attached the fisheye shader without its corresponding script I was getting the same exact results as you are with your current shader code. If you dont have access to the image effects package let me know and ill send your the fisheye scripts and shaders.
I have tried several ways so far. The shader itself work very well when I add it to a plane located in front of main Camera. But when I add it to the main Camera by below code, nothing could be visible on the screen! (just a blank screen) without any error message. I assign the above shader to repl variable.
using UnityEngine;
using System.Collections;
public class test2 : MonoBehaviour {
// Use this for initialization
public Shader repl = null;
void Start () {
Camera.main.SetReplacementShader(repl,"Opaque");
}
// Update is called once per frame
void Update () {
}
}
Just for your information, the above shader distorts the scene to a trapezium shape.