teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

1

反転対策案を追記

2020/02/21 21:08

投稿

Bongo
Bongo

スコア10816

answer CHANGED
@@ -1,2 +1,215 @@
1
1
  もしかして「Edit」→「Project Settings...」の「Other Settings」→「Graphics APIs for Mac」が「Metal」の時だけ反転したりしますかね?
2
- 試しに`ScreenShot`スクリプトの「Camera Event」を一段階手前の「After Forward Alpha」に変更してみるとどうでしょうか。もしそれでは改善しない、あるいは他の問題が発生するようでしたら何か別の手を考えてみます。
2
+ 試しに`ScreenShot`スクリプトの「Camera Event」を一段階手前の「After Forward Alpha」に変更してみるとどうでしょうか。もしそれでは改善しない、あるいは他の問題が発生するようでしたら何か別の手を考えてみます。
3
+
4
+ **ScreenShot改造案**
5
+
6
+ まず下記のようなY軸反転シェーダーを用意して...
7
+
8
+ ```ShaderLab
9
+ Shader "Hidden/FlipY"
10
+ {
11
+ Properties
12
+ {
13
+ _MainTex ("Texture", 2D) = "white" {}
14
+ }
15
+ SubShader
16
+ {
17
+ Cull Off ZWrite Off ZTest Always
18
+
19
+ Pass
20
+ {
21
+ CGPROGRAM
22
+ #pragma vertex vert
23
+ #pragma fragment frag
24
+
25
+ #include "UnityCG.cginc"
26
+
27
+ struct appdata
28
+ {
29
+ float4 vertex : POSITION;
30
+ float2 uv : TEXCOORD0;
31
+ };
32
+
33
+ struct v2f
34
+ {
35
+ float2 uv : TEXCOORD0;
36
+ float4 vertex : SV_POSITION;
37
+ };
38
+
39
+ v2f vert(appdata v)
40
+ {
41
+ v2f o;
42
+ o.vertex = UnityObjectToClipPos(v.vertex);
43
+ o.uv = v.uv;
44
+ o.uv.y = 1.0 - o.uv.y;
45
+ return o;
46
+ }
47
+
48
+ sampler2D _MainTex;
49
+
50
+ fixed4 frag(v2f i) : SV_Target
51
+ {
52
+ return tex2D(_MainTex, i.uv);
53
+ }
54
+ ENDCG
55
+ }
56
+ }
57
+ }
58
+ ```
59
+
60
+ `ScreenShot`にフィールドを追加、`CreateBuffer`メソッド中にY軸反転処理を組み込んでみました。
61
+
62
+ ```C#
63
+ using System.Collections;
64
+ using System.Collections.Generic;
65
+ using UnityEngine;
66
+ using System.IO;
67
+ using UnityEngine.Rendering;
68
+ using System;
69
+
70
+ public class ScreenShot : MonoBehaviour
71
+ {
72
+
73
+ public event System.Action<RenderTexture> OnCaptured;
74
+
75
+ [SerializeField, Tooltip("GUIをレンダリングしているカメラ")]
76
+ private Camera _guiCamera = null;
77
+
78
+ [SerializeField, Tooltip("キャプチャするタイミング")]
79
+ private CameraEvent _cameraEvent = CameraEvent.BeforeImageEffects;
80
+
81
+ [SerializeField, Tooltip("合成時に無視されるUIのレイヤー")]
82
+ private LayerMask _captureTargetLayer = -1;
83
+
84
+ [SerializeField, Tooltip("ARカメラ")]
85
+ private Camera _mainCamera = null;
86
+
87
+ // ここに前述のY反転シェーダーをセットしておく
88
+ [SerializeField, Tooltip("Y反転シェーダー")]
89
+ private Shader _flipYShader;
90
+ private Material _flipYMaterial;
91
+
92
+ private RenderTexture _buf = null;
93
+ private CommandBuffer _commandBuffer = null;
94
+
95
+ #region ### MonoBehaviour ###
96
+
97
+ private void Awake()
98
+ {
99
+ CreateBuffer();
100
+ }
101
+ /// <summary>
102
+ /// 動作確認用にGizmoでテクスチャを表示する
103
+ /// </summary>
104
+ private void OnGUI()
105
+ {
106
+ if (_buf == null) return;
107
+ GUI.DrawTexture(new Rect(5f, 5f, Screen.width * 0.5f, Screen.height * 0.5f), _buf);
108
+ }
109
+
110
+ public void OnClick()
111
+ {
112
+ TakeScreenshot();
113
+ }
114
+
115
+ #endregion ### MonoBehaviour ###
116
+
117
+ /// <summary>
118
+ /// バッファを生成する
119
+ /// </summary>
120
+ private void CreateBuffer()
121
+ {
122
+ _buf = new RenderTexture(Screen.width, Screen.height, 0);
123
+
124
+ _commandBuffer = new CommandBuffer();
125
+ _commandBuffer.name = "CaptureScene";
126
+ if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Metal)
127
+ {
128
+ // Metal上で動作している場合、CurrentActiveを一旦別のレンダーテクスチャに写し取り
129
+ // それをさらにY反転シェーダーを通して_bufにレンダリングする
130
+ if (_flipYMaterial == null)
131
+ {
132
+ _flipYMaterial = new Material(_flipYShader);
133
+ }
134
+ int tempTexture = Shader.PropertyToID("_TempTex");
135
+ _commandBuffer.GetTemporaryRT(tempTexture, -1, -1);
136
+ _commandBuffer.Blit(BuiltinRenderTextureType.CurrentActive, tempTexture);
137
+ _commandBuffer.Blit(tempTexture, _buf, _flipYMaterial);
138
+ _commandBuffer.ReleaseTemporaryRT(tempTexture);
139
+ }
140
+ else
141
+ {
142
+ _commandBuffer.Blit(BuiltinRenderTextureType.CurrentActive, _buf);
143
+ }
144
+ }
145
+
146
+ /// <summary>
147
+ /// スクリーンショットを撮影する
148
+ /// </summary>
149
+ public void TakeScreenshot()
150
+ {
151
+ AddCommandBuffer();
152
+
153
+ StartCoroutine(WaitCapture());
154
+ }
155
+
156
+ /// <summary>
157
+ /// コマンドバッファの処理を待つ
158
+ /// </summary>
159
+ private IEnumerator WaitCapture()
160
+ {
161
+ yield return new WaitForEndOfFrame();
162
+
163
+ BlendGUI();
164
+
165
+ if (OnCaptured != null)
166
+ {
167
+ OnCaptured.Invoke(_buf);
168
+ }
169
+
170
+ RemoveCommandBuffer();
171
+ }
172
+
173
+ /// <summary>
174
+ /// GUI要素をブレンドする
175
+ /// </summary>
176
+ private void BlendGUI()
177
+ {
178
+ _guiCamera.targetTexture = _buf;
179
+
180
+ int tmp = _guiCamera.cullingMask;
181
+ _guiCamera.cullingMask = _captureTargetLayer;
182
+
183
+ _guiCamera.Render();
184
+
185
+ _guiCamera.cullingMask = tmp;
186
+
187
+ _guiCamera.targetTexture = null;
188
+ }
189
+
190
+ /// <summary>
191
+ /// メインカメラにコマンドバッファを追加する
192
+ /// </summary>
193
+ private void AddCommandBuffer()
194
+ {
195
+ _mainCamera.AddCommandBuffer(_cameraEvent, _commandBuffer);
196
+ }
197
+
198
+ /// <summary>
199
+ /// メインカメラからコマンドバッファを削除する
200
+ /// </summary>
201
+ private void RemoveCommandBuffer()
202
+ {
203
+ if (_mainCamera == null)
204
+ {
205
+ return;
206
+ }
207
+
208
+ _mainCamera.RemoveCommandBuffer(_cameraEvent, _commandBuffer);
209
+ }
210
+
211
+ }
212
+ ```
213
+
214
+ `_commandBuffer.Blit(BuiltinRenderTextureType.CurrentActive, _buf, _flipYMaterial);`とせずにわざわざ`tempTexture`に写してから反転していますが、これは`CurrentActive`がテクスチャではなくレンダーバッファであるケースを考慮したものです。
215
+ どうやら`Blit`はソース側がテクスチャでない場合、シェーダーを通してのレンダリングができないために代わりに単純な[ビットブリット](https://en.wikipedia.org/wiki/Bit_blit)的な処理([Texture2D.ReadPixels](https://docs.unity3d.com/jp/current/ScriptReference/Texture2D.ReadPixels.html)のようなもの?)を行うらしく、独自のマテリアルを使っての加工ができなそうだったためあのようにしました。