質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

1回答

7338閲覧

[Unity]深度バッファを使って表面のみに投影テクスチャマッピング

torano

総合スコア92

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2019/03/24 23:35

編集2019/03/24 23:41

やりたいこと

3Dモデルのテクスチャに直接書き込むようなペイントアプリを作成したいと思っています。方法としては、メインカメラの他にカメラを用意しそこからレイをとばしヒットした場所に投影テクスチャマッピングしてレンダーテクスチャに直接書き込もうと考えています。(毎フレームテクスチャでスタンプしていくようなイメージです。)
投影テクスチャマッピングしたものをそのモデルのテクスチャに反映する部分で苦戦していたのですが、ありがたいことにその問題はここで質問し、解決することができました。
[Unity]Graphics.Blitで投影テクスチャマッピングシェーダーをテクスチャに反映できない

問題点

しかし次の問題として、投影テクスチャマッピングの仕様上カメラから見えない場所(モデルの凹凸に隠れる場所や、裏側など)にも投影されてしまうことに気づきました。以下のようなものです。
イメージ説明
イメージ説明
Gameビューをマウスでクリックし、そこからレイをとばしてヒットポイントに塗っているのですが、ご覧の通り塗られるべきでない場所にも塗られてしまっているので、解決したいです。

試したこと

いろいろと調べてみた結果、深度バッファを使えば解決できそうだということがわかりました。カメラからピクセルまでの距離と深度情報を比べて深度のほうが小さければ塗らない、といった具合です。そして幸運にも似たようなことをやっている人を発見しました。
Unity デプスシャドウ技法を自前で書いて影を落としてみる
しかし、これを真似て実装してみたのですがうまくいきませんでした。

スクリプトと再現方法

前回の質問したときのスクリプトを、解決法にそって改善し、自分なりに上のことを実現しようとしたスクリプトです。前回と同様、塗られるモデルにこのスクリプトをアタッチし、各種パラメータを設定します。projectorには空のゲームオブジェクトにCameraコンポーネントを付けたものを入れます。Cameraの設定はOcclusion Culling, Allow HDR, Allow MSAAをオフにしてます。(orthoSizeなどはスクリプトから調節するようにしてます。)
前回と違い、今回は深度情報を使いたかったので疑似ではなくCameraコンポーネントを使用しています。またモデルには他の適当なマテリアル(スタンダードシェーダなど)を使用しています。Bakeファンクションで実際の塗りが発生しますが、これはテスト用のスクリプトなので毎フレーム塗っています。

先のデプスシャドウの実装では、予めRenderTextureをつくりCameraのTargetTextureにつけているようですが、この方法は自分ではなぜかうまく深度値がとれなかったのでスクリプトで動的にバッファを作成しカメラをDepthモードに切り替えています。

おそらくカメラからピクセルまでの距離か深度情報どちらかの値が間違っていると思うのですが、もしかしたら深度がうまくとれていないのかもしれません...
深度を扱うのは今回がはじめてで試行錯誤しながらやっていて、よくある例のようにポストエフェクトでメインカメラからの深度を直接画面に反映する方法で深度テクスチャの方法はうまくいっていました。しかし今回のようにシーンにカメラを二つ使用し、サブカメラのほうで深度をとろうとするとうまくいかなかったです。そもそも深度テクスチャはほぼ真っ黒なので、Inspectorだけではうまく深度とれてるか確認できず、デバッグのために深度テクスチャをGraphics.Blitで他のシェーダ(rの値が0と1意外の値のとき色をはっきりさせるもの)を使ってコピーし、uGUIで画面に表示してみたのですが、うまく深度テクスチャがコピーできなかったです...

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4using UnityEngine.Rendering; 5 6public class DepthProjection : MonoBehaviour 7{ 8 [SerializeField] Color brushColor; 9 [SerializeField] Texture brushTex; 10 [SerializeField] GameObject projector; 11 [SerializeField] float zNear = 0.01f, zFar = 1, orthoSize = 0.5f; 12 [SerializeField] Material paintMat; 13 14 Material objMat; 15 Renderer renderer; 16 RenderTexture modelMainRT, colorBufferRT, depthBufferRT; 17 public RenderTexture depthTex 18 { 19 get 20 { 21 return depthBufferRT; 22 } 23 } 24 25 int ProjectorVPMat, brushColorId, projTexId, depthTexId; 26 CommandBuffer bakeCommand; 27 Camera projCam; 28 29 private void OnValidate() 30 { 31 SetProjCam(); 32 } 33 34 void SetProjCam() 35 { 36 if (projCam) 37 { 38 projCam.orthographic = true; 39 projCam.orthographicSize = orthoSize; 40 projCam.nearClipPlane = zNear; 41 projCam.farClipPlane = zFar; 42 projCam.aspect = 1; 43 } 44 } 45 46 private void Awake() 47 { 48 renderer = GetComponent<Renderer>(); 49 objMat = renderer?.material; 50 ProjectorVPMat = Shader.PropertyToID("ProjectorVPMat"); 51 brushColorId = Shader.PropertyToID("_BrushColor"); 52 projTexId = Shader.PropertyToID("_BrushTex"); 53 depthTexId = Shader.PropertyToID("_DepthTex"); 54 55 projCam = projector?.GetComponent<Camera>(); 56 57 if (projCam == null) 58 { 59 Debug.LogAssertion("projector needs Camera Component"); 60 } 61 62 SetProjCam(); 63 64 InitProjectorAndDepthTexture(); 65 InitCommand(); 66 67 } 68 69 void InitProjectorAndDepthTexture() 70 { 71 if (objMat) 72 { 73 var mainTex = objMat.mainTexture; 74 75 if (mainTex && projCam) 76 { 77 modelMainRT = new RenderTexture(mainTex.width, mainTex.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default); 78 79 projCam.depthTextureMode = DepthTextureMode.Depth; 80 81 colorBufferRT = new RenderTexture(projCam.pixelWidth, projCam.pixelHeight, 0, RenderTextureFormat.ARGB32); 82 colorBufferRT.Create(); 83 depthBufferRT = new RenderTexture(projCam.pixelWidth, projCam.pixelHeight, 24, RenderTextureFormat.Depth); 84 depthBufferRT.Create(); 85 86 projCam.SetTargetBuffers(colorBufferRT.colorBuffer, depthBufferRT.depthBuffer); 87 88 Graphics.Blit(mainTex, modelMainRT); 89 objMat.mainTexture = modelMainRT; 90 91 if (paintMat) 92 { 93 paintMat.mainTexture = modelMainRT; 94 paintMat.SetTexture(depthTexId, depthBufferRT); 95 } 96 } 97 } 98 } 99 100 void InitCommand() 101 { 102 if (renderer & modelMainRT & paintMat) 103 { 104 105 var c = new CommandBuffer(); 106 c.name = "Bake Projection Command"; 107 108 var RTBufferId = Shader.PropertyToID("_RTBuffer"); 109 110 c.GetTemporaryRT(RTBufferId, modelMainRT.descriptor); 111 c.Blit(modelMainRT, RTBufferId); 112 c.SetRenderTarget(RTBufferId); 113 c.DrawRenderer(renderer, paintMat); 114 c.Blit(RTBufferId, modelMainRT); 115 c.ReleaseTemporaryRT(RTBufferId); 116 117 bakeCommand = c; 118 } 119 } 120 121 void SetMatProperties() 122 { 123 if (projector && paintMat && projCam != null) 124 { 125 Matrix4x4 projMat = GL.GetGPUProjectionMatrix(projCam.projectionMatrix, true); 126 Matrix4x4 viewMat = projCam.worldToCameraMatrix; 127 128 Matrix4x4 vpMat = projMat * viewMat; 129 130 paintMat.SetMatrix(ProjectorVPMat, vpMat); 131 paintMat.SetColor(brushColorId, brushColor); 132 paintMat.SetTexture(projTexId, brushTex); 133 } 134 } 135 136 // Update is called once per frame 137 void Update() 138 { 139 SetMatProperties(); 140 Bake(); 141 } 142 143 public void Bake() 144 { 145 if (bakeCommand != null) 146 { 147 Graphics.ExecuteCommandBuffer(bakeCommand); 148 } 149 } 150 151 private void OnDestroy() 152 { 153 ReleaseAllRT(); 154 } 155 156 void ReleaseAllRT() 157 { 158 ReleaseRT(modelMainRT); 159 ReleaseRT(colorBufferRT); 160 ReleaseRT(depthBufferRT); 161 } 162 163 void ReleaseRT(RenderTexture rt) 164 { 165 if (rt) 166 { 167 rt.Release(); 168 } 169 } 170} 171

深度を利用した投影テクスチャマッピングのシェーダーコードです。

Shader "Painter/Test/DepthProjectionTest" { Properties { _MainTex("Main Texture", 2D) = "white" { } _BrushColor("Brush Color", Color) = (1, 1, 1, 1) _BrushTex("Projection Texture", 2D) = "white" { } _DepthTex("Depth Texture", 2D) = "white" { } } SubShader { Tags { "RenderType" = "Opaque" } LOD 100 Pass { // 裏を向いた面に対応するためカリングを切る Cull Off CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex: POSITION; float2 uv: TEXCOORD0; }; struct v2f { float2 uv: TEXCOORD0; float4 vertex: SV_POSITION; float4 projUV: TEXCOORD1; float4 depthVertex : TEXCOORD2; }; sampler2D _MainTex; sampler2D _BrushTex; sampler2D _DepthTex; float4 _MainTex_ST; float4 _BrushColor; uniform float4x4 ProjectorVPMat; v2f vert(appdata v) { v2f o; // ベイク時はメッシュのUV座標を頂点の位置と見なして描画させる float2 position = v.uv * 2.0 - 1.0; #if UNITY_UV_STARTS_AT_TOP // DirectX系の場合は上下を逆転させる position.y *= -1.0; #endif o.vertex = float4(position, 0.0, 1.0); o.depthVertex = ComputeGrabScreenPos(mul(ProjectorVPMat, v.vertex)); // SkinnedMeshRendererに対応するため、モデル行列はUnity組み込みのものを用いる float4x4 mvpMat = mul(ProjectorVPMat, unity_ObjectToWorld); o.projUV = ComputeGrabScreenPos(mul(mvpMat, v.vertex)); o.uv = TRANSFORM_TEX(v.uv, _MainTex); return o; } fixed4 frag(v2f i) : SV_Target { fixed4 projColor = fixed4(0, 0, 0, 0); // depth textureから深度値をサンプリング。線形になるようLinear01Depthを使ったほうがいい? fixed4 depthColor = tex2D(_DepthTex, i.depthVertex.xy); float diff = i.depthVertex.z - depthColor.r; if (diff <= 0 && i.projUV.w > 0.0) { // projection to screen space i.projUV.x /= i.projUV.w; i.projUV.y /= i.projUV.w; if (i.projUV.x >= 0 && i.projUV.x <= 1 && i.projUV.y >= 0 && i.projUV.y <= 1) { projColor = tex2D(_BrushTex, i.projUV); } } fixed4 mainColor = tex2D(_MainTex, i.uv); // if _BrushColor.a = 0 or projColor.a = 0, then mainColor. if both 1, then _BrushColor. return mainColor * (1 - _BrushColor.a * projColor.a) + _BrushColor * _BrushColor.a * projColor.a; } ENDCG } } }

思いついた方法が新しくカメラを用意し深度テクスチャを使う、というものだったのですが、もし他に良い方法があれば教えてほしいです。できれば二度レンダリングしたくないので...

環境

Unity 2018.3.9f1で作成したプロジェクトです。(レンダリング設定はforwardだと思います。)
プラットフォームはデフォルトのものです。

よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

stdio

2019/03/25 01:07

同じUV情報を使ってないならテクスチャ新調した方が速な感じがするのですが...
torano

2019/03/25 01:15

どういうことでしょうか?
stdio

2019/03/25 01:27

冷たいアドバイスになりますが、 この程度の事が分からずに深度バッファをしているなら、もう一度勉強してきた方が良いですよ。
guest

回答1

0

ベストアンサー

深度バッファを使うというのは的確なアイディアだと思います。確かに状況がシャドウマッピングとよく似ていますね。

おそらく、うまくいかなかった原因はデプステクスチャのサンプリング位置の狂いのような気がします。それを含め、いくつか変更を加えてみました。まずC#スクリプト側は...

  • デプステクスチャの解像度としてprojCam.pixelWidthprojCam.pixelHeightを使うと、サイズは画面解像度と同じになるかと思います。

それはそれでいいとも思いますが、個人的には画面サイズによってエッジの精密さが変わってしまうのが気になり、さしあたりbrushTex.widthbrushTex.heightを使ってブラシテクスチャと同サイズにしました。これでは荒すぎるようでしたら、brushTex.width * 4といった風に何倍かしてやるのもいいでしょう。

  • projCamenabledSetProjCam内でfalseに変更して自動的レンダリングはさせないようにし、代わりにベイク用コマンドバッファ実行部分の手前で明示的にレンダリングさせるようにしました。順序的にはこちらの方が自然じゃないでしょうかね?

当初のコードですと、レンダリングされたデプスがベイク結果に反映されるのは次回ベイク時...つまり次のフレームになると思われます。

  • projCamの描画に綺麗なシェーディングを施す必要はないので、projCam.renderingPathRenderingPath.VertexLitに差し替えました。スカイボックスもいらないでしょうから切っています。

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4using UnityEngine.Rendering; 5 6public class DepthProjection : MonoBehaviour 7{ 8 [SerializeField] Color brushColor; 9 [SerializeField] Texture brushTex; 10 [SerializeField] GameObject projector; 11 [SerializeField] float zNear = 0.01f, zFar = 1, orthoSize = 0.5f; 12 [SerializeField] Material paintMat; 13 14 Material objMat; 15 Renderer renderer; 16 RenderTexture modelMainRT, colorBufferRT, depthBufferRT; 17 public RenderTexture depthTex 18 { 19 get 20 { 21 return depthBufferRT; 22 } 23 } 24 25 int ProjectorVPMat, brushColorId, projTexId, depthTexId; 26 CommandBuffer bakeCommand; 27 Camera projCam; 28 29 private void OnValidate() 30 { 31 SetProjCam(); 32 } 33 34 void SetProjCam() 35 { 36 if (projCam) 37 { 38 projCam.orthographic = true; 39 projCam.orthographicSize = orthoSize; 40 projCam.nearClipPlane = zNear; 41 projCam.farClipPlane = zFar; 42 projCam.aspect = 1; 43 44 // カメラコンポーネントを非アクティブ化して自動レンダリングをさせなくする 45 // また、projCam上でレンダリングする際のパスをVertexLitに差し替える 46 // スカイボックスも描画不要 47 projCam.enabled = false; 48 projCam.renderingPath = RenderingPath.VertexLit; 49 projCam.clearFlags = CameraClearFlags.Depth; 50 } 51 } 52 53 private void Awake() 54 { 55 renderer = GetComponent<Renderer>(); 56 objMat = renderer?.material; 57 ProjectorVPMat = Shader.PropertyToID("ProjectorVPMat"); 58 brushColorId = Shader.PropertyToID("_BrushColor"); 59 projTexId = Shader.PropertyToID("_BrushTex"); 60 depthTexId = Shader.PropertyToID("_DepthTex"); 61 62 projCam = projector?.GetComponent<Camera>(); 63 64 if (projCam == null) 65 { 66 Debug.LogAssertion("projector needs Camera Component"); 67 } 68 69 SetProjCam(); 70 71 InitProjectorAndDepthTexture(); 72 InitCommand(); 73 74 } 75 76 void InitProjectorAndDepthTexture() 77 { 78 if (objMat) 79 { 80 var mainTex = objMat.mainTexture; 81 82 if (mainTex && projCam) 83 { 84 modelMainRT = new RenderTexture(mainTex.width, mainTex.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default); 85 86 projCam.depthTextureMode = DepthTextureMode.Depth; 87 88 // デプステクスチャのサイズをブラシテクスチャと同サイズに変更 89 var depthBufferWidth = brushTex.width; 90 var depthBufferHeight = brushTex.height; 91 colorBufferRT = new RenderTexture(depthBufferWidth, depthBufferHeight, 0, RenderTextureFormat.ARGB32); 92 colorBufferRT.Create(); 93 depthBufferRT = new RenderTexture(depthBufferWidth, depthBufferHeight, 24, RenderTextureFormat.Depth); 94 depthBufferRT.Create(); 95 96 projCam.SetTargetBuffers(colorBufferRT.colorBuffer, depthBufferRT.depthBuffer); 97 98 Graphics.Blit(mainTex, modelMainRT); 99 objMat.mainTexture = modelMainRT; 100 101 if (paintMat) 102 { 103 paintMat.mainTexture = modelMainRT; 104 paintMat.SetTexture(depthTexId, depthBufferRT); 105 } 106 } 107 } 108 } 109 110 void InitCommand() 111 { 112 if (renderer & modelMainRT & paintMat) 113 { 114 115 var c = new CommandBuffer(); 116 c.name = "Bake Projection Command"; 117 118 var RTBufferId = Shader.PropertyToID("_RTBuffer"); 119 120 c.GetTemporaryRT(RTBufferId, modelMainRT.descriptor); 121 c.Blit(modelMainRT, RTBufferId); 122 c.SetRenderTarget(RTBufferId); 123 c.DrawRenderer(renderer, paintMat); 124 c.Blit(RTBufferId, modelMainRT); 125 c.ReleaseTemporaryRT(RTBufferId); 126 127 bakeCommand = c; 128 } 129 } 130 131 void SetMatProperties() 132 { 133 if (projector && paintMat && projCam != null) 134 { 135 Matrix4x4 projMat = GL.GetGPUProjectionMatrix(projCam.projectionMatrix, true); 136 Matrix4x4 viewMat = projCam.worldToCameraMatrix; 137 138 Matrix4x4 vpMat = projMat * viewMat; 139 140 paintMat.SetMatrix(ProjectorVPMat, vpMat); 141 paintMat.SetColor(brushColorId, brushColor); 142 paintMat.SetTexture(projTexId, brushTex); 143 } 144 } 145 146 // Update is called once per frame 147 void Update() 148 { 149 SetMatProperties(); 150 Bake(); 151 } 152 153 public void Bake() 154 { 155 // このタイミングでprojCamを手動描画 156 projCam.Render(); 157 158 if (bakeCommand != null) 159 { 160 Graphics.ExecuteCommandBuffer(bakeCommand); 161 } 162 } 163 164 private void OnDestroy() 165 { 166 ReleaseAllRT(); 167 } 168 169 void ReleaseAllRT() 170 { 171 ReleaseRT(modelMainRT); 172 ReleaseRT(colorBufferRT); 173 ReleaseRT(depthBufferRT); 174 } 175 176 void ReleaseRT(RenderTexture rt) 177 { 178 if (rt) 179 { 180 rt.Release(); 181 } 182 } 183}

次に、シェーダー側は...

  • depthVertexは廃止しました。ブラシテクスチャとデプステクスチャはともに同じUV座標をサンプリングするべきだと思いますので、ともにprojUVを使用することにしました。
  • フラグメントの前後判定の際に、環境によるクリップ空間の違いを考慮するようにしてみました。おそらくこれでいけるかとは思うのですが、もし前後判定の狂いがあるようでしたら、どのような描画結果になってしまったか、グラフィックスシステムは何を使っているかコメントいただければ、可能であれば直してみようと思います。

ご提示いただいた渋谷ほととぎす通信さんの製作例の途中に、デプステクスチャの内容を画面上に表示してみせている画像がありますが、そこではカメラに近い部分ほど色が暗くなっています。一方、【Unity】【シェーダ】カメラから見た深度を描画する - LIGHT11にもデプスの可視化を試みている例がありますが、こちらは逆に近い部分が明るくなっています。プラットフォーム特有のレンダリングの違い - Unity マニュアルの「クリップスペース座標」や「シェーダーの深度 (Z) の向き」をご覧いただくと、ごちゃごちゃぶりを察していただけるでしょうか...

ShaderLab

1Shader "Painter/Test/DepthProjectionTest" 2{ 3 Properties 4 { 5 _MainTex("Main Texture", 2D) = "white" { } 6 _BrushColor("Brush Color", Color) = (1, 1, 1, 1) 7 _BrushTex("Projection Texture", 2D) = "white" { } 8 _DepthTex("Depth Texture", 2D) = "white" { } 9 } 10 11 SubShader 12 { 13 Tags { "RenderType" = "Opaque" } 14 LOD 100 15 16 Pass 17 { 18 // 裏を向いた面に対応するためカリングを切る 19 Cull Off 20 21 CGPROGRAM 22 23 #pragma vertex vert 24 #pragma fragment frag 25 26 #include "UnityCG.cginc" 27 28 struct appdata 29 { 30 float4 vertex: POSITION; 31 float2 uv: TEXCOORD0; 32 }; 33 34 struct v2f 35 { 36 float2 uv: TEXCOORD0; 37 float4 vertex: SV_POSITION; 38 float4 projUV: TEXCOORD1; 39 }; 40 41 sampler2D _MainTex; 42 sampler2D _BrushTex; 43 sampler2D _DepthTex; 44 float4 _MainTex_ST; 45 float4 _BrushColor; 46 47 uniform float4x4 ProjectorVPMat; 48 49 v2f vert(appdata v) 50 { 51 v2f o; 52 53 // ベイク時はメッシュのUV座標を頂点の位置と見なして描画させる 54 float2 position = v.uv * 2.0 - 1.0; 55 #if UNITY_UV_STARTS_AT_TOP 56 // DirectX系の場合は上下を逆転させる 57 position.y *= -1.0; 58 #endif 59 o.vertex = float4(position, 0.0, 1.0); 60 61 // SkinnedMeshRendererに対応するため、モデル行列はUnity組み込みのものを用いる 62 float4x4 mvpMat = mul(ProjectorVPMat, unity_ObjectToWorld); 63 o.projUV = ComputeGrabScreenPos(mul(mvpMat, v.vertex)); 64 o.uv = TRANSFORM_TEX(v.uv, _MainTex); 65 return o; 66 } 67 68 fixed4 frag(v2f i) : SV_Target 69 { 70 // 環境に応じて奥行き値を加工する際に使う 71 #ifdef UNITY_REVERSED_Z 72 float inverseZ = 1.0; 73 #else 74 float inverseZ = 0.0; 75 #endif 76 77 fixed4 projColor = fixed4(0, 0, 0, 0); 78 float depth = 1.0 - inverseZ; 79 80 if (i.projUV.w > 0.0) 81 { 82 // projection to screen space 83 i.projUV /= i.projUV.w; 84 85 if (i.projUV.x >= 0 && i.projUV.x <= 1 && i.projUV.y >= 0 && i.projUV.y <= 1) 86 { 87 // _BrushTexも_DepthTexも同じ位置をサンプリングする 88 projColor = tex2D(_BrushTex, i.projUV); 89 depth = tex2D(_DepthTex, i.projUV).r; 90 } 91 } 92 93 // まずブラシ視点からのクリップ座標z(w除算済み)とdepthを、奥が1・手前が0となるよう加工する 94 float near = UNITY_NEAR_CLIP_VALUE; 95 float far = 1.0 - inverseZ; 96 float normalizedZ = (i.projUV.z - near) / (far - near); 97 float normalizedDepth = inverseZ + (1.0 - 2.0 * inverseZ) * depth; 98 99 // 両者を比較して前後を判断する 100 // 判定に落ちたフラグメントはclipで破棄する 101 // また、Zファイティング現象防止のため、判定値は少しだけ増やしてやる 102 clip(normalizedDepth - normalizedZ + 0.001); 103 104 fixed4 mainColor = tex2D(_MainTex, i.uv); 105 106 // if _BrushColor.a = 0 or projColor.a = 0, then mainColor. if both 1, then _BrushColor. 107 return mainColor * (1 - _BrushColor.a * projColor.a) + _BrushColor * _BrushColor.a * projColor.a; 108 } 109 ENDCG 110 } 111 } 112}

投稿2019/03/25 11:18

Bongo

総合スコア10807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

torano

2019/03/28 02:27

回答ありがとうございます。DirectX12で試してみたのですが一応動きました。 シェーダーコードを一通り見たのですがクリップ空間のところで混乱しています。DirectXだとUNITY_REVERSED_Zが定義されフラグメントシェーダーでの計算結果はnear=0、far=0となるのですよね?コードみた時点ではおかしくなると思ったのですが正常に動いています。なぜなんでしょうか? 公式リファレンスのプラットフォームの違いのところをみたのですが、 「クリップスペース座標」では Direct3D 類:クリップ空間の深度の範囲は、ニアクリップ面の 0.0 からファークリップ面の +1.0 までになります。 このようにあります。 「シェーダーの深度 (Z) の向き」では DirectX12のクリップ空間範囲は、 [near,0] さらに、「他のプラットフォーム: 伝来の方向」では クリップ空間は、Direct3D のようなプラットフォームでは、範囲は [0,far] さらに下には以下のようにあります。 そのため、逆方向深度 (Z) を使ったプラットフォームのシェーダーを使用する場合は(略)クリップ空間範囲は “near” (ニア) から 0 (ファー) の範囲内です。 これをみて結局1(near)から0(far)なのか逆なのか混乱してわからなかったのですが、 https://docs.unity3d.com/ja/current/Manual/SL-BuiltinMacros.html こちらではUNITY_NEAR_CLIP_VALUEはDirect3D のようなプラットフォームでは 0.0とあるので0(near)から1(far)と思っていましたがどうなんでしょう。
torano

2019/03/28 03:57

よくよく考えてみたらわかりました。DirectXだとnear=1でそれ以外のDirect3Dだとnear=0ということなんですね。UNITY_NEAR_CLIP_VALUEもHLSLSupport.cgincみたらDirectXだと1になってました。 リファレンスの説明だけみたらすごくわかりずらいですね... 他に質問です。 i.projUV /= i.projUV.w; float normalizedZ = (i.projUV.z - near) / (far - near); ここではzにもwで割っているのでx, yが0から1のときzも0から1だと思うのですが、それをnormalizedZとしてそのまま使わないのはなぜなのでしょう?
Bongo

2019/03/28 06:00

その点については、wで割っただけでは同次座標が3次元座標につぶれるのみで、環境による違い...手前と奥が-1~1か1~0か...といった差異は残るかと思います。 といいますのも、projUVはクリッピング座標をComputeGrabScreenPosで加工したものですが、これはUnityCG.cgincをご覧いただきますと... inline float4 ComputeGrabScreenPos (float4 pos) { #if UNITY_UV_STARTS_AT_TOP float scale = -1.0; #else float scale = 1.0; #endif float4 o = pos * 0.5f; o.xy = float2(o.x, o.y*scale) + o.w; #ifdef UNITY_SINGLE_PASS_STEREO o.xy = TransformStereoScreenSpaceTex(o.xy, pos.w); #endif o.zw = pos.zw; return o; } このようになっており、zとwは元のクリッピング座標の値のまま残されるようです。そのため別途環境に応じた対策を加えてから使用することにしました。
torano

2019/03/29 05:29

なるほどです。今回もありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問