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

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

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

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

Unity

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

Q&A

解決済

1回答

4098閲覧

[Unity]Graphics.Blitで投影テクスチャマッピングシェーダーをテクスチャに反映できない

torano

総合スコア92

Unity3D

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

Unity

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

1グッド

0クリップ

投稿2019/03/07 23:20

編集2019/03/07 23:30

何がしたいか

3Dモデルのテクスチャに直接書き込むようなペイントアプリを作成しようとしています。書き込む方法として、投影テクスチャマッピング用のシェーダーをGraphics.Blitを使いテクスチャに適用します。カメラからレイをとばしヒットした場所に書き込むようにするのですが、投影テクスチャリング用に疑似カメラを用意し、そのカメラのプロパティをシェーダーに渡すことで、特定の位置に書き込む(テクスチャを投影する)ようにしています。(投影テクスチャマッピングではなく、単にヒットした位置のUVをシェーダーに渡しその位置を中心にしてブラシ用のテクスチャも張り付ける方法も考え、試してみましたが、モデルのUVに大きく依存するので辞めました。)

何ができなかったか

投影テクスチャリングは実現できた(シェーダーコードは下にあります)のですが、Graphics.Blitで結果をテクスチャに直接反映しようとするとうまくいきません。一応塗られますが、位置が全然違います。

試したこと

まず投影テクスチャリングが失敗の原因なのかわからなかったので、以下のように、投影と投影後に動的にテクスチャに書き込むことができるスクリプトをつくってみました。このスクリプトで投影できることは確認できたのですが、投影した場所にテクスチャが書き込まれません。

スクリプトの説明と再現方法

投影されるモデルにこのスクリプトと投影用のシェーダーでつくったマテリアルを付けます。マテリアルのプロパティでブラシ用(投影される)テクスチャを適当に設定します。さらに、空のGameObjectをつくり、モデルのインスペクタからprojectorに設定します。色を設定し、プレイモードにすると、そのprojectorのオブジェクトのz方向に投影が行われます。インスペクタのねじマークからBakeを押すとGraphics.Blitによりテクスチャに投影結果が塗られます。

このスクリプトでは単に、オブジェクトのテクスチャーをレンダーテクスチャに置き換え、モデルのTransformからモデル変換行列、projectorからビュー行列、疑似カメラ用のプロパティからプロジェクション変換行列を毎フレームつくりシェーダー渡し、インスペクタからBakeが押された時だけGraphics.Blitするようにしています。本来はマウスクリックでレイを飛ばし、そのときだけ投影かつGraphics.Blitしますがこれは実験用のスクリプトですので。

Bakeしてみると、変な場所、左上あたり(uvでいう(0, 1)あたり)を中心に塗られてしまいます。projectorの位置やスケールを動かしてもほとんど塗られる位置に変更はありませんが、回転を変更するとすこし変化があることを確認しました。また、fovを変えると塗られる大きさが変わるみたいです。(ここは正しい?)どうやら、普通のレンダリングとGraphics.Blit時のものが違うためなのが原因みたいですが、どうやれば直るのかわからないです...
以下はBakeしたときの画像です。
イメージ説明
上にある通り単純なUVを使ったペイントでも同じようにGraphics.Blitでテクスチャに適用していましたが正常に動いていたので、レンダーテクスチャの一連の流れ(メインテクスチャおきかえ、バッファを利用しBlitで書き込みなど)は間違っていないような気がします。渡す行列をBlitのために特別に変える必要があるのか(シェーダー内で?)よくわからないです。

Graphics.Blitを使わずにテクスチャに変更を加える方法もあるにはあるみたいですが、いろいろ制約がありそうなのでできればこれを使いたいです。例えばGraphics.DrawMeshNowがありますが、いくつかmeshがある場合その数だけ呼ばなければならなかったりSkinnedMeshRendererの場合BakeMeshしないといけないみたいです。一応想定しているモデルはSkinnedMeshRendererコンポーネントをいくつかもつものになります。

環境など

Unity 2018.3.0f1(アップデート予定)
VRアプリで考えてますが、とりあえずwindowsでプラットフォーム設定はデフォルトです。

何日かググって調べましたがどうしてもわかりませんでした。どうかよろしくお願いいたします。

C#

1using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 using UnityEditor; 5 6 public class ProjectionTest : MonoBehaviour { 7 [SerializeField] GameObject projector; 8 [SerializeField] Color brushColor; 9 [SerializeField] float fov = 20, aspect = 1, zNear = 0.01f, zFar = 1; 10 11 Material mat; 12 int MVPMatPropertyId, brushColorId; 13 RenderTexture renderTexture; 14 15 private void Awake() { 16 mat = GetComponent<Renderer>()?.material; 17 MVPMatPropertyId = Shader.PropertyToID("MVPMatForProjection"); 18 brushColorId = Shader.PropertyToID("_BrushColor"); 19 20 InitCanvas(); 21 } 22 23 void InitCanvas() { 24 25 var mainTex = mat.mainTexture; 26 27 // generate RenderTexture 28 renderTexture = new RenderTexture(mainTex.width, mainTex.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default); 29 // copy the main texture to the RenderTexture 30 Graphics.Blit(mainTex, renderTexture); 31 32 // replace the main texture on the material with the RenderTexture 33 mat.mainTexture = renderTexture; 34 } 35 36 void SetMatProperties() { 37 if (mat && projector) { 38 Matrix4x4 modelMat = transform.localToWorldMatrix; 39 Matrix4x4 projMat = GL.GetGPUProjectionMatrix(Matrix4x4.Perspective(fov, aspect, zNear, zFar), true); 40 41 // camera space matches OpenGL convention: camera's forward is the negative Z axis, so negate all of the 3rd row values of the matrix 42 // https://docs.unity3d.com/ja/current/ScriptReference/Camera-worldToCameraMatrix.html 43 Matrix4x4 viewMat = Matrix4x4.TRS(projector.transform.position, projector.transform.rotation, projector.transform.lossyScale).inverse; 44 viewMat.m20 *= -1f; 45 viewMat.m21 *= -1f; 46 viewMat.m22 *= -1f; 47 viewMat.m23 *= -1f; 48 49 50 Matrix4x4 mvpMat = projMat * viewMat * modelMat; // this is the same as UNITY_MATRIX_MVP in shader writing 51 mat.SetMatrix(MVPMatPropertyId, mvpMat); 52 mat.SetColor(brushColorId, brushColor); 53 } 54 } 55 56 // Update is called once per frame 57 void Update() { 58 SetMatProperties(); 59 } 60 61 [ContextMenu("Bake")] 62 void Bake() { 63 64 Debug.Log("paint"); 65 66 67 var renderTextureBuffer = RenderTexture.GetTemporary(renderTexture.width, renderTexture.height); // buffer 68 69 70 Debug.Log("blit"); 71 72 Graphics.Blit(renderTexture, renderTextureBuffer, mat); // first, copy renderTexture, which the painterMat will be applied to, to renderTextureBuffer because the texture can't be changed directly (and apply the material after copied 73 Graphics.Blit(renderTextureBuffer, renderTexture); // then, copy the buffer to renderTexture 74 75 } 76 }

ShaderLab

1Shader "Custom/Painter/ProjectionPaint" 2 { 3 Properties 4 { 5 _MainTex("Main Texture", 2D) = "white" {} 6 _BrushColor("Brush Color", Color) = (1, 1, 1, 1) 7 _ProjTex("Projection Texture", 2D) = "white" {} 8 } 9 SubShader 10 { 11 Tags { "RenderType" = "Opaque" } 12 LOD 100 13 14 Pass 15 { 16 CGPROGRAM 17 #pragma vertex vert 18 #pragma fragment frag 19 20 #include "UnityCG.cginc" 21 22 struct appdata 23 { 24 float4 vertex : POSITION; 25 float2 uv : TEXCOORD0; 26 }; 27 28 struct v2f 29 { 30 float2 uv : TEXCOORD0; 31 float4 vertex : SV_POSITION; 32 float4 projUV : TEXCOORD1; 33 }; 34 35 sampler2D _MainTex; 36 sampler2D _ProjTex; 37 float4 _MainTex_ST; 38 float4 _BrushColor; 39 40 uniform float4x4 MVPMatForProjection; 41 42 v2f vert(appdata v) 43 { 44 v2f o; 45 o.vertex = UnityObjectToClipPos(v.vertex); 46 o.projUV = ComputeGrabScreenPos(mul(MVPMatForProjection, v.vertex)); 47 o.uv = TRANSFORM_TEX(v.uv, _MainTex); 48 return o; 49 } 50 51 fixed4 frag(v2f i) : SV_Target 52 { 53 fixed4 projColor = fixed4(0, 0, 0, 0); 54 55 if (i.projUV.w > 0.0) { 56 // projection to screen space 57 i.projUV.x /= i.projUV.w; 58 i.projUV.y /= i.projUV.w; 59 60 if (i.projUV.x >= 0 && i.projUV.x <= 1 && i.projUV.y >= 0 && i.projUV.y <= 1) { 61 projColor = tex2D(_ProjTex, i.projUV); 62 } 63 64 } 65 66 fixed4 mainColor = tex2D(_MainTex, i.uv); 67 68 // if _BrushColor.a = 0 or projColor.a = 0, then mainColor. if both 1, then _BrushColor. 69 return mainColor * (1 - _BrushColor.a * projColor.a) + _BrushColor * _BrushColor.a * projColor.a; 70 } 71 ENDCG 72 } 73 } 74 }
Bongo👍を押しています

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

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

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

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

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

guest

回答1

0

ベストアンサー

DrawMeshNowは避けたい...とのことですが、おそらくBlitのみではむしろ困難(あるいは無理?)のような気がします。
3D画面上でブラシによって塗られた領域と、そこに対応するテクスチャ上の位置を結び付けるにはモデルのUV情報が欲しいところですが、Blitでの描画は単なる四角い板を描いているようなものでしょうから、UV座標を使った対応付けができないんじゃないでしょうか?
すでにDrawMesh系統を使った例を色々調査なさったご様子ですが、基本的な方針はそれらをご参考にするのが妥当のように思います。そして、いくつもメッシュがあれば多分それぞれについて描画を行わせることになるだろうと思います。ただ、いくらかは効率よくすることは可能かもしれません。思いついただけで実験してはいませんが、例えばブラシの視錐台と交差するモデルだけを抜き出して処理する...とかでしょうかね?

SkinnedMeshRenderer対応についてですが、DrawMeshNowの代わりにCommandBufferのDrawRendererを使えばメッシュのベイクを行わなくても塗れそうな感じでした。
ご質問者さんのコードを下記のように改造し、どうなるか試してみました。

C#

1using System.Collections; 2using System.Collections.Generic; 3using System.Linq; 4using UnityEngine; 5using UnityEditor; 6using UnityEngine.Rendering; 7 8public class ProjectionTest : MonoBehaviour 9{ 10 [SerializeField] GameObject projector; 11 [SerializeField] Color brushColor; 12 [SerializeField] Texture2D projectionTexture; 13 [SerializeField] float fov = 20, aspect = 1, zNear = 0.01f, zFar = 1; 14 15 Renderer[] renderers; 16 Material[][] materials; 17 RenderTexture[][] renderTextures; 18 CommandBuffer bakeCommand; 19 int VPMatPropertyId, brushColorId; 20 21 private void Awake() 22 { 23 var shader = Shader.Find("Custom/Painter/ProjectionPaint"); 24 25 // 自身だけでなく子階層からもレンダラーを探す 26 renderers = GetComponentsInChildren<Renderer>(); 27 28 // 一つのレンダラーが複数個のマテリアルを持っている場合も考慮した 29 var projTexId = Shader.PropertyToID("_ProjTex"); 30 materials = renderers.Select( 31 r => 32 { 33 return r.materials.Select(m => 34 { 35 m.shader = shader; 36 m.SetTexture(projTexId, projectionTexture); 37 return m; 38 }).ToArray(); 39 }).ToArray(); 40 41 VPMatPropertyId = Shader.PropertyToID("VPMatForProjection"); 42 brushColorId = Shader.PropertyToID("_BrushColor"); 43 44 InitCanvas(); 45 } 46 47 void InitCanvas() 48 { 49 // メインテクスチャと同数のレンダーテクスチャを用意する 50 var existingRenderTextures = new Dictionary<Texture, RenderTexture>(); 51 renderTextures = materials.Select( 52 mArray => 53 { 54 return mArray.Select(m => { 55 var mainTex = m.mainTexture; 56 RenderTexture rt; 57 58 if (!existingRenderTextures.TryGetValue(mainTex, out rt)) 59 { 60 // generate RenderTexture 61 rt = new RenderTexture( 62 mainTex.width, 63 mainTex.height, 64 0, 65 RenderTextureFormat.ARGB32, 66 RenderTextureReadWrite.Default); 67 // copy the main texture to the RenderTexture 68 Graphics.Blit(mainTex, rt); 69 existingRenderTextures.Add(mainTex, rt); 70 } 71 72 // replace the main texture on the material with the RenderTexture 73 m.mainTexture = rt; 74 return rt; 75 }).ToArray(); 76 }).ToArray(); 77 78 // Bake内で行っていた作業に相当する操作を行うコマンドバッファを作成する 79 var c = new CommandBuffer(); 80 var tempBufferId = Shader.PropertyToID("_TempBuffer"); 81 for (var i = 0; i < renderTextures.Length; i++) 82 { 83 var rtArray = renderTextures[i]; 84 var r = renderers[i]; 85 var mArray = materials[i]; 86 87 for (var j = 0; j < rtArray.Length; j++) 88 { 89 var rt = rtArray[j]; 90 var m = mArray[j]; 91 // 作業用のレンダーテクスチャ(_TempBuffer)を作成 92 c.GetTemporaryRT(tempBufferId, rt.descriptor); 93 // まず背景として、rtを加工せずにそのまま_TempBufferに描画 94 c.Blit(rt, tempBufferId); 95 // キーワードをオンにしてベイクモードに切り替え、j番目のサブメッシュを描画 96 c.EnableShaderKeyword("BAKE_PAINT"); 97 c.SetRenderTarget(tempBufferId); 98 c.DrawRenderer(r, m, j); 99 c.DisableShaderKeyword("BAKE_PAINT"); 100 // 結果をrtに戻す 101 c.Blit(tempBufferId, rt); 102 c.ReleaseTemporaryRT(tempBufferId); 103 } 104 105 106 } 107 bakeCommand = c; 108 } 109 110 void SetMatProperties() 111 { 112 if (projector) 113 { 114 Matrix4x4 projMat = GL.GetGPUProjectionMatrix(Matrix4x4.Perspective(fov, aspect, zNear, zFar), true); 115 116 // camera space matches OpenGL convention: camera's forward is the negative Z axis, so negate all of the 3rd row values of the matrix 117 // https://docs.unity3d.com/ja/current/ScriptReference/Camera-worldToCameraMatrix.html 118 Matrix4x4 viewMat = Matrix4x4.TRS(projector.transform.position, projector.transform.rotation, projector.transform.lossyScale).inverse; 119 viewMat.m20 *= -1f; 120 viewMat.m21 *= -1f; 121 viewMat.m22 *= -1f; 122 viewMat.m23 *= -1f; 123 124 // SkinnedMeshRendererはTransformのモデル行列とは異なった 125 // 加工済みの行列を使うはずなので、ここではモデル行列は合成しない 126 Matrix4x4 vpMat = projMat * viewMat; 127 foreach (var m in materials.SelectMany(mArray=>mArray)) 128 { 129 m.SetMatrix(VPMatPropertyId, vpMat); 130 m.SetColor(brushColorId, brushColor); 131 } 132 } 133 } 134 135 // Update is called once per frame 136 void Update() 137 { 138 SetMatProperties(); 139 } 140 141 [ContextMenu("Bake")] 142 void Bake() 143 { 144 Debug.Log("bake"); 145 Graphics.ExecuteCommandBuffer(bakeCommand); 146 } 147}

ShaderLab

1Shader "Custom/Painter/ProjectionPaint" 2{ 3 Properties 4 { 5 _MainTex ("Main Texture", 2D) = "white" { } 6 _BrushColor ("Brush Color", Color) = (1, 1, 1, 1) 7 _ProjTex ("Projection Texture", 2D) = "white" { } 8 } 9 10 SubShader 11 { 12 Tags { "RenderType" = "Opaque" } 13 LOD 100 14 15 Pass 16 { 17 // 裏を向いた面に対応するためカリングを切る 18 Cull Off 19 20 CGPROGRAM 21 22 #pragma vertex vert 23 #pragma fragment frag 24 25 // キーワードBAKE_PAINTあり・なしでマルチコンパイル 26 #pragma multi_compile _ BAKE_PAINT 27 28 #include "UnityCG.cginc" 29 30 struct appdata 31 { 32 float4 vertex: POSITION; 33 float2 uv: TEXCOORD0; 34 }; 35 36 struct v2f 37 { 38 float2 uv: TEXCOORD0; 39 float4 vertex: SV_POSITION; 40 float4 projUV: TEXCOORD1; 41 }; 42 43 sampler2D _MainTex; 44 sampler2D _ProjTex; 45 float4 _MainTex_ST; 46 float4 _BrushColor; 47 48 uniform float4x4 VPMatForProjection; 49 50 v2f vert(appdata v) 51 { 52 v2f o; 53 54 // ベイク時はメッシュのUV座標を頂点の位置と見なして描画させる 55 #ifdef BAKE_PAINT 56 float2 position = v.uv * 2.0 - 1.0; 57 #if UNITY_UV_STARTS_AT_TOP 58 // DirectX系の場合は上下を逆転させる 59 position.y *= -1.0; 60 #endif 61 o.vertex = float4(position, 0.0, 1.0); 62 #else 63 o.vertex = UnityObjectToClipPos(v.vertex); 64 #endif 65 66 // SkinnedMeshRendererに対応するため、モデル行列はUnity組み込みのものを用いる 67 float4x4 mvpMat = mul(VPMatForProjection, unity_ObjectToWorld); 68 o.projUV = ComputeGrabScreenPos(mul(mvpMat, v.vertex)); 69 o.uv = TRANSFORM_TEX(v.uv, _MainTex); 70 return o; 71 } 72 73 fixed4 frag(v2f i): SV_Target 74 { 75 fixed4 projColor = fixed4(0, 0, 0, 0); 76 77 if(i.projUV.w > 0.0) 78 { 79 // projection to screen space 80 i.projUV.x /= i.projUV.w; 81 i.projUV.y /= i.projUV.w; 82 83 if (i.projUV.x >= 0 && i.projUV.x <= 1 && i.projUV.y >= 0 && i.projUV.y <= 1) 84 { 85 projColor = tex2D(_ProjTex, i.projUV); 86 } 87 } 88 89 fixed4 mainColor = tex2D(_MainTex, i.uv); 90 91 // if _BrushColor.a = 0 or projColor.a = 0, then mainColor. if both 1, then _BrushColor. 92 return mainColor * (1 - _BrushColor.a * projColor.a) + _BrushColor * _BrushColor.a * projColor.a; 93 } 94 ENDCG 95 } 96 } 97}

まずMeshFilterの場合の実験として、下図のようなUVマップを持つスザンヌに対し...

スザンヌのUVマップ

ブラシの狙いを頭部右上周辺に合わせ...

ブラシの狙い

ベイクすると下図の位置が赤く着色されました。

着色されたレンダーテクスチャ

そしてSkinnedMeshRendererでもやってみようと思い、ユニティちゃんの右肩周辺を狙ってみましたが...

ブラシの狙い

一応塗ることはできたものの、左半身にも色が飛び散ってしまいました。

左半身にも色が付着してしまった

ユニティちゃんにはUVを共有する面がかなりあるようで、このままでは半身だけを塗るのは無理そうです。
正しく塗るには、面が重ならないよう再配置したUV座標を用意してやる必要があるでしょう。もしかしたらライトマップ用UVをうまく使えば可能かもしれません。Unwrappingなんて機能もあるみたいですが、これはエディタ上でのみ使用可能なようですね。実行時にやりたい場合には、自前でUVを再配置してやることになりそうです...

着色されたレンダーテクスチャ

投稿2019/03/09 03:06

Bongo

総合スコア10811

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

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

torano

2019/03/09 06:06

とても詳しい解答をありがとうございます。 質問投稿してからもずっと模索しつづけていたのですが、自分でもBlitでは無理そうだなということがわかりました。Blitの内部がどうなっているのか調べていたら、おそらく以下のようなことをやっているみたいでした。 RenderTexture.active = destination; material.SetTexture("_MainTex", source); GL.PushMatrix(); GL.LoadOrtho(); material.SetPass(0); GL.Begin(GL.QUADS); GL.MultiTexCoord2(0, 0.0f, 0.0f); GL.Vertex3(0.0f, 0.0f, 0.0f); GL.MultiTexCoord2(0, 0.0f, 1.0f); GL.Vertex3(0.0f, 1.0f, 0.0f); GL.MultiTexCoord2(0, 1.0f, 1.0f); GL.Vertex3(1.0f, 1.0f, 0.0f); GL.MultiTexCoord2(0, 1.0f, 0.0f); GL.Vertex3(1.0f, 0.0f, 0.0f); GL.End(); GL.PopMatrix(); GLクラスのことはよくわからないのですが、おそらくRenderTextureの頂点位置をGL.Vertex3(0.0f, 0.0f, 0.0f);で設定していて、それがシェーダに渡されている=実際のモデルの頂点位置とは異なる。このために頂点位置を使って計算している投影シェーダがうまくいかないのかな、と思いました。(一応、他にも頂点のワールド位置を直接そのピクセルの色に設定するシェーダを書いて毎フレームBlitしてみましたが常に色固定だったのでそういうことなんだと思います。) こういうわけで、DrawMesh使うしかないのかなと考えていたのですが、CommandBufferにDrawRendererなんてメソッドがあったんですね!! いくつかメッシュがある場合の効率化についてですが、SkinnedMeshRendererを複数もち、かつIKなどで動かすかもしれないモデルをもともと想定していたので、それに対して効率よくレイキャストを行うために高速でBVHを構築するスクリプトをつくっていました。塗りたい範囲はそんなに広くないはずなので、塗る範囲をコライダーにし、そのBVHをつかって衝突したBoundがもつSkinnedMeshRendererを特定できるので「塗るSkinnedMeshRendererのみに対し処理をする」という課題はクリアできそうです。また、UVの重なりも、回答者様がおっしゃるようにUV2を使うことを想定していました。 すばらしい解答、本当にありがとうございます。まだ試してないのですが、さっそく使ってみたいと思います。
torano

2019/03/09 07:19

すいません、ひとつわからないことがあったのですが、シェーダにてベイク時にuvを頂点にしているのはなぜでしょうか? position = v.uv * 2.0 - 1.0の部分もよくわからないのですが、さらにその(position, 0, 1)を頂点にしている理由がわからないです。 o.vertexがモデルの最終的な頂点の位置になるという認識なのですが、これではすべてのzが0になっておかしくなると思うのにならないのはなぜなんでしょう。。。
Bongo

2019/03/09 18:01

おっしゃる通りzが0ですので、前後に平たく押しつぶされた2Dの図形を描いていることになりますね。 ご存じでしたらすみませんが、最終的に各頂点座標は出力先(画面やレンダーテクスチャ)の中心が(0, 0, 0)、左端のxが-1、右端のxが+1、下端のyが-1、上端のyが+1になる座標系に押し込めてやることになります。zについてはOpenGLやDirectXで微妙に違うそうですが、ともかく-1~+1あるいは0~+1の範囲に詰められます。 普段の3D描画だと、基本的にはメッシュの元々の頂点座標にモデル・ビュー・プロジェクション変換を施して、この正規化された空間に押し縮めていることになります。このへんの過程については色々な解説サイトがあるかと思いますが、例えば http://www.opengl-tutorial.org/jp/beginners-tutorials/tutorial-3-matrices/ の後半には図入りの解説があってイメージしやすそうでした。 今回の場合、通常の描画時とベイク描画時でフラグメントシェーダーは同じ...つまりメッシュへの色付けの仕方は同じまま、頂点の位置を通常時はいつもの立体的に見える位置に、ベイク時はUV展開図の位置になるようにしています。頂点の位置だけ展開図の形にすることで、塗られる位置もその場所に対応するUV展開図上の位置になる...という理屈です。 すでにご覧になったかもしれませんが、http://edom18.hateblo.jp/entry/2018/11/08/084143 の記事の中程にある https://twitter.com/i/status/1059671503115939840 のムービーがわかりやすく感じました。 先ほど申し上げたモデル・ビュー・プロジェクション変換ですが、これはべつに必ず行わなければならないものではなく、とにかく最終的に-1~+1の直方体に押し込めた座標を出力しさえすればいいのです。 メッシュに埋め込まれたUV座標は、通常は縦横それぞれ0~1の2次元座標になっていると思います(テクスチャが端でクリッピングされたり繰り返されたりすることを見越して、あえてはみ出した座標を使うテクニックもありますが...そのようなモデルも、今回のやり方では正しく塗ることができないでしょう)。ですので、それを単純に縦横2倍に引き延ばしてから中心が(0, 0)になるようにずらしてやれば0~+1の範囲が-1~+1の範囲に変換され、描画先...つまりレンダーテクスチャ全面にUV展開図を描くことができるというわけです。
torano

2019/03/14 07:47

ようやく理解できました。ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.37%

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

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

質問する

関連した質問