前提・実現したいこと
物体(削られる側)と対象A(削る側)が位置しており、物体に対象Aが接触すると物体が削れていく3D Unityを作成したい。
ゲームでは3Dで彫刻ができるイメージで、物体(削られる側)対象A(削る側)はblenderで出力された.objが使えればよいと考えています。
2020.4にBongoさんが回答されていたスクリプトhttps://teratail.com/questions/252026?sort=2#3794を拝見しましたが、応用できなかったので質問させていただきました。unityで3Dの切り取りは可能でしょうか。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/11/20 01:48
回答3件
0
パート3
削り役の棍棒もシーンに配置し、こちらには下記スクリプトをアタッチしました。ですが実際のところこちらのスクリプトは削り取りを実演してみせる上での脇役で、マウス操作に応じて棍棒を動かし、スペースキーで削り取りを実行する機能しかありません。
lang
1using System.Collections; 2using UnityEngine; 3 4[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))] 5public class Carver : MonoBehaviour 6{ 7 [SerializeField] private Carvee target; 8 [SerializeField][Range(0.0f, 50.0f)] private float rotationSpeed = 10.0f; 9 [SerializeField][Range(0.0f, 5.0f)] private float pushPullSpeed = 1.0f; 10 [SerializeField] private Color inactiveColor = Color.green; 11 [SerializeField] private Color activeColor = Color.red; 12 13 private Material carverMaterial; 14 private Mesh carverMesh; 15 private float distance; 16 private float horizontalAngle; 17 private float verticalAngle; 18 19 private IEnumerator Start() 20 { 21 yield return null; 22 Cursor.lockState = CursorLockMode.Locked; 23 var meshFilter = this.GetComponent<MeshFilter>(); 24 this.carverMesh = meshFilter.sharedMesh; 25 var meshRenderer = this.GetComponent<MeshRenderer>(); 26 this.carverMaterial = meshRenderer.material; 27 var relativePosition = this.transform.position - this.target.Center; 28 this.distance = relativePosition.magnitude; 29 var direction = relativePosition.normalized; 30 this.verticalAngle = Mathf.Asin(direction.y) * Mathf.Rad2Deg; 31 this.horizontalAngle = Mathf.Atan2(direction.z, direction.x) * Mathf.Rad2Deg; 32 } 33 34 private void Update() 35 { 36 if (this.carverMesh == null) 37 { 38 return; 39 } 40 41 // マウス左右で水平に、上下で垂直に回転し 42 // 左ボタン・右ボタンで前後に動かす 43 var mouseX = Input.GetAxis("Mouse X"); 44 var mouseY = Input.GetAxis("Mouse Y"); 45 var push = Input.GetMouseButton(0) ? this.pushPullSpeed * Time.deltaTime : 0.0f; 46 var pull = Input.GetMouseButton(1) ? this.pushPullSpeed * Time.deltaTime : 0.0f; 47 this.distance = Mathf.Max(0.0f, (this.distance - push) + pull); 48 this.horizontalAngle = (this.horizontalAngle + (mouseX * this.rotationSpeed)) % 360.0f; 49 this.verticalAngle = Mathf.Clamp(this.verticalAngle + (mouseY * this.rotationSpeed), -90.0f, 90.0f); 50 var h = this.horizontalAngle * Mathf.Deg2Rad; 51 var v = this.verticalAngle * Mathf.Deg2Rad; 52 var sinH = Mathf.Sin(h); 53 var cosH = Mathf.Cos(h); 54 var sinV = Mathf.Sin(v); 55 var cosV = Mathf.Cos(v); 56 var direction = new Vector3(cosH * cosV, sinV, sinH * cosV); 57 var center = this.target.Center; 58 this.transform.position = center + (direction * this.distance); 59 this.transform.LookAt(center); 60 61 // スペースキーを押している間、毎フレームCarveを行う 62 if (Input.GetKey(KeyCode.Space)) 63 { 64 this.carverMaterial.color = this.activeColor; 65 this.target.Carve(this.carverMesh, this.transform); 66 } 67 else 68 { 69 this.carverMaterial.color = this.inactiveColor; 70 } 71 } 72 73 private void OnDestroy() 74 { 75 Cursor.lockState = CursorLockMode.None; 76 } 77}
プレイモードにすると、Carvee
が元々のメッシュをベースにモアイ像の断面を描き、ボリュームテクスチャを生成します。
スクリプト内のvolume
は下図のようになっており、Carve
実行によってボクセルを消していくことで削られたように見せようと考えました。
削る様子はこんな感じです。彫刻...というにはちょっと精密さに欠け、貴重な遺物を破壊しているだけになってしまいました。
弱点としては、やはりある程度の解像度がないと精密に削れない点でしょうね。今回はvoxelsPerUnit
が128で、身長をおよそ2mに調整した状態でボクセル化しており、テクスチャのサイズは120×95×272です。元々のモアイ像は下図のようになめらかですが...
今回の程度のボクセル密度だと、実行時の外観は3Dプリンターで成形したばかりのようにデコボコです。
かといってもっと解像度を上げようにも、縦横高さが2倍になると体積は8倍に膨れ上がってしまいますから簡単にはいかないでしょうね...
また、彫り上がったものを保存する機能は省略しました。保存するとしたらボリュームデータとしてそのまま保存するか、あるいはマーチングキューブ法だとかで表面を張り、メッシュに変換してやることになるかと思います。
投稿2021/11/22 10:27
編集2021/11/22 10:31総合スコア10811
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
パート2
断面描画用、およびシーン上での描画用に下記シェーダーを作成しました。
ShaderLab
1Shader "Volume/Slice" 2{ 3 Properties 4 { 5 _MainTex ("Texture", 2D) = "white" {} 6 } 7 SubShader 8 { 9 // メッシュの断面を描くパス 10 // 両面描画とし、表面なら透明、裏面なら白で塗る 11 // これを使い、閉じたポリゴンモデルをニアクリップ面で切断するような構図で 12 // 描画すると、切断面から裏面を覗く形になって断面の形が描かれるはず 13 Pass 14 { 15 Cull Off 16 17 CGPROGRAM 18 #pragma vertex vert 19 #pragma fragment frag 20 #include "UnityCG.cginc" 21 22 float4 vert(float4 vertex : POSITION) : SV_POSITION 23 { 24 return UnityObjectToClipPos(vertex); 25 } 26 27 fixed4 frag(fixed faceDirection : VFACE) : SV_Target 28 { 29 return (1.0 - faceDirection) * 0.5; 30 } 31 ENDCG 32 } 33 34 // 断面を削り取るパス 35 // 削られる側の断面画像を削る側の断面画像で削る際に使う 36 // 削る側の断面を白黒反転し、削られる側の断面に 37 // 乗算合成することで削り取る 38 Pass 39 { 40 Cull Off 41 ZWrite Off 42 ZTest Always 43 Blend DstColor Zero 44 45 CGPROGRAM 46 #pragma vertex vert 47 #pragma fragment frag 48 #include "UnityCG.cginc" 49 50 struct appdata 51 { 52 float4 vertex : POSITION; 53 float2 uv : TEXCOORD0; 54 }; 55 56 struct v2f 57 { 58 float4 vertex : SV_POSITION; 59 float2 uv : TEXCOORD0; 60 }; 61 62 v2f vert(appdata v) 63 { 64 v2f o; 65 o.vertex = UnityObjectToClipPos(v.vertex); 66 o.uv = v.uv; 67 return o; 68 } 69 70 sampler2D _MainTex; 71 72 fixed4 frag(v2f i) : SV_Target 73 { 74 return 1.0 - tex2D(_MainTex, i.uv); 75 } 76 ENDCG 77 } 78 } 79} 80
ShaderLab
1Shader "Volume/Volume" 2{ 3 Properties 4 { 5 _MainTex ("Volume", 3D) = "white" {} 6 _Color ("Color", Color) = (1.0, 1.0, 1.0, 1.0) 7 } 8 SubShader 9 { 10 Tags { "Queue" = "AlphaTest" "RenderType" = "Opaque" } 11 12 Pass 13 { 14 Tags { "LightMode" = "ForwardBase" } 15 16 Cull Back 17 ZTest Always 18 19 CGPROGRAM 20 #pragma vertex vert 21 #pragma fragment frag 22 #pragma multi_compile_fwdbase 23 #include "UnityCG.cginc" 24 #include "Lighting.cginc" 25 26 #define DIRECTIONAL_EPSILON 0.0009765625 27 #define INITIAL_STEP_LENGTH 0.03125 28 #define TERMINAL_EXTENSION (INITIAL_STEP_LENGTH * 2.0) 29 #define STEP_COUNT 64 30 #define EXTRA_DEPTH_BIAS 0.03125 31 32 sampler3D _MainTex; 33 fixed4 _Color; 34 float3 _VoxelSize; 35 float3 _BoundsSize; 36 float4x4 _VolumeToLocal; 37 float4x4 _LocalToVolume; 38 sampler2D _CameraDepthTexture; 39 40 float3 worldToObjectPos(float3 worldPos) {return mul(unity_WorldToObject, float4(worldPos, 1.0)).xyz;} 41 float3 objectToWorldPos(float3 localPos) {return mul(unity_ObjectToWorld, float4(localPos, 1.0)).xyz;} 42 float3 worldToObjectVec(float3 worldVec) {return mul((float3x3)unity_WorldToObject, worldVec);} 43 float3 objectToWorldVec(float3 localVec) {return mul((float3x3)unity_ObjectToWorld, localVec);} 44 float3 getWorldCameraPos() {return UNITY_MATRIX_I_V._14_24_34;} 45 float3 getWorldCameraDir() {return -UNITY_MATRIX_V._31_32_33;} 46 bool isPerspective() {return any(UNITY_MATRIX_P._41_42_43);} 47 48 float3 getWorldCameraOrigin(float3 worldPos) 49 { 50 return isPerspective() 51 ? getWorldCameraPos() 52 : worldPos + dot(getWorldCameraPos() - worldPos, getWorldCameraDir()) * getWorldCameraDir(); 53 } 54 55 float3 getVolumeBoundsFaces(float3 volumePos, float3 volumeViewDir, float faceOffset) 56 { 57 float3 signs = sign(volumeViewDir); 58 return -(signs * (volumePos - 0.5) + faceOffset) / (abs(volumeViewDir) + (1.0 - abs(signs)) * DIRECTIONAL_EPSILON); 59 } 60 61 float getVolumeBoundsFrontFace(float3 volumePos, float3 volumeViewDir) 62 { 63 float3 lengths = getVolumeBoundsFaces(volumePos, volumeViewDir, 0.5); 64 return max(max(max(lengths.x, lengths.y), lengths.z), 0.0); 65 } 66 67 float sampleOpaqueZ(float4 screenPos) 68 { 69 float rawDepth = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(screenPos)); 70 return isPerspective() 71 ? LinearEyeDepth(rawDepth) 72 : -dot(unity_CameraInvProjection._33_34, float2(_ProjectionParams.x * (rawDepth * 2.0 - 1.0), 1.0)); 73 } 74 75 // worldPosからworldViewDir方向を見た時の、ボリューム空間における衝突点を求める 76 // wはヒットした場合1.0、ヒットしなかった場合-1.0となる 77 float4 getVolumeContact(float3 worldPos, float3 worldViewDir, float4 screenPos) 78 { 79 float3 worldOrigin = getWorldCameraOrigin(worldPos); 80 float3 localOrigin = worldToObjectPos(worldOrigin); 81 float3 volumeOrigin = mul(_LocalToVolume, float4(localOrigin, 1.0)).xyz; 82 float maxZ = min(dot(worldPos - worldOrigin, worldViewDir), sampleOpaqueZ(screenPos)); 83 float3 worldTerminal = worldOrigin + worldViewDir * maxZ; 84 float3 localTerminal = worldToObjectPos(worldTerminal); 85 float3 volumeTerminal = mul(_LocalToVolume, float4(localTerminal, 1.0)).xyz; 86 float3 volumeViewDir = normalize(volumeTerminal - volumeOrigin); 87 float3 volumeContact = volumeOrigin; 88 float terminalDistance = distance(volumeOrigin, volumeTerminal) + TERMINAL_EXTENSION; 89 float progress = getVolumeBoundsFrontFace(volumeOrigin, volumeViewDir); 90 float step = INITIAL_STEP_LENGTH; 91 float sign = 1.0; 92 float hit = -1.0; 93 [loop] 94 for (int i = 0; i < STEP_COUNT; i++) 95 { 96 progress += step; 97 if (progress > terminalDistance) 98 { 99 hit = -1.0; 100 break; 101 } 102 volumeContact = volumeOrigin + volumeViewDir * progress; 103 if ((tex3D(_MainTex, volumeContact).r - 0.5) * sign > 0.0) 104 { 105 hit = 1.0; 106 sign *= -1.0; 107 step *= -0.5; 108 } 109 } 110 return float4(volumeContact, hit); 111 } 112 113 // ボリューム空間上の座標から、その周辺を観察してワールド法線を求める 114 // 傾きを算出できなかった場合はゼロベクトルを返す 115 float3 getWorldNormal(float3 volumePos) 116 { 117 float3 volumeNormal; 118 volumeNormal.x = tex3D(_MainTex, volumePos - float3(_VoxelSize.x, 0.0, 0.0)).r - tex3D(_MainTex, volumePos + float3(_VoxelSize.x, 0.0, 0.0)).r; 119 volumeNormal.y = tex3D(_MainTex, volumePos - float3(0.0, _VoxelSize.y, 0.0)).r - tex3D(_MainTex, volumePos + float3(0.0, _VoxelSize.y, 0.0)).r; 120 volumeNormal.z = tex3D(_MainTex, volumePos - float3(0.0, 0.0, _VoxelSize.z)).r - tex3D(_MainTex, volumePos + float3(0.0, 0.0, _VoxelSize.z)).r; 121 float3 localNormal = mul(volumeNormal, (float3x3)_LocalToVolume); 122 float3 worldNormal = mul(localNormal, (float3x3)unity_WorldToObject); 123 float normalSqrLength = dot(worldNormal, worldNormal); 124 if (normalSqrLength > 0.0) 125 { 126 return worldNormal / sqrt(normalSqrLength); 127 } 128 return 0.0; 129 } 130 131 struct v2f 132 { 133 float4 vertex : SV_POSITION; 134 float3 worldPos : TEXCOORD0; 135 float3 worldViewDir : TEXCOORD1; 136 float4 screenPos : TEXCOORD2; 137 }; 138 139 v2f vert(float4 vertex : POSITION) 140 { 141 v2f o; 142 o.vertex = UnityObjectToClipPos(vertex); 143 o.worldPos = objectToWorldPos(vertex.xyz); 144 o.worldViewDir = isPerspective() ? -UnityWorldSpaceViewDir(o.worldPos) : getWorldCameraDir(); 145 o.screenPos = ComputeScreenPos(o.vertex); 146 return o; 147 } 148 149 half4 frag(v2f i) : SV_Target 150 { 151 float4 contact = getVolumeContact(i.worldPos, normalize(i.worldViewDir), i.screenPos); 152 clip(contact.w); 153 float3 worldNormal = getWorldNormal(contact.xyz); 154 155 // ディレクショナルライト1個のランバートライティングで 156 // 影も省略した簡易な(手抜きの)陰影付け 157 half3 color = max(dot(_WorldSpaceLightPos0.xyz, worldNormal), 0.0) * _LightColor0 * _Color.rgb + ShadeSH9(half4(worldNormal, 1.0)); 158 return half4(color, 1.0); 159 } 160 ENDCG 161 } 162 } 163}
モアイ像はスケールを調整して身長2mぐらいでシーン上に配置しました。
モアイ像にCarvee
をアタッチし、これらシェーダーファイルをsliceShader
、volumeShader
にセットしています。
なお、もう一つMoaiController
というものがアタッチされていますが、これは下記のようにモアイ像をくるくる回すだけのもので、本題の削り取りとは関係ありません。
lang
1using UnityEngine; 2 3public class MoaiController : MonoBehaviour 4{ 5 [SerializeField][Range(0.0f, 90.0f)] private float angularSpeed = 90.0f; 6 7 private void Update() 8 { 9 this.transform.Rotate(0.0f, this.angularSpeed * Time.deltaTime, 0.0f, Space.World); 10 } 11}
(パート3へ続く...)
投稿2021/11/22 10:26
総合スコア10811
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
ベストアンサー
モデルを細かいドットの集合体として扱うとおっしゃるのも候補として適切だと思います。ボクセル的なアプローチと言えるかもしれませんね。
その方針でどうなるか試してみました。実験材料として、まず削られる役は大英博物館によるモアイ像を...
削る役として自前でちょこっと作った謎の棍棒を用意しました。
モアイ像をインポートすると、ガワとなるゲームオブジェクトの中に「default」という名称のメッシュオブジェクトが入った構造のプレハブになるかと思います。このdefaultを取り出して「Moai」と名前を変え、下記スクリプトをアタッチしました。
lang
1using UnityEngine; 2using UnityEngine.Rendering; 3 4[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))] 5public class Carvee : MonoBehaviour 6{ 7 [SerializeField] private Shader sliceShader; // ここにインスペクター上で断面描画用シェーダーをセットする 8 [SerializeField] private Shader volumeShader; // ここにインスペクター上でボリューム描画用シェーダーをセットする 9 [SerializeField][Min(0)] private int voxelsPerUnit = 128; // 1mあたりのボクセル数 10 [SerializeField] private Color color = Color.white; 11 12 private Bounds volumeBounds; 13 private Material initialMaterial; 14 private Material sliceMaterial; 15 private Material volumeMaterial; 16 private Matrix4x4 orthoMatrix; 17 private Matrix4x4 sliceMatrix; 18 private Mesh sourceMesh; 19 private Mesh volumeMesh; 20 private MeshFilter meshFilter; 21 private MeshRenderer meshRenderer; 22 private RenderTexture slice0; 23 private RenderTexture slice1; 24 private RenderTexture volume; 25 private readonly Vector3[] carverBoundsVertices = new Vector3[8]; 26 private Vector3 initialScale; 27 private Vector3 voxelSize; 28 private Vector3Int textureSizeInt; 29 30 public Vector3 Center => this.transform.TransformPoint(this.volumeBounds.center); 31 32 private void Start() 33 { 34 // 本来のメッシュのバウンディングボックス、オブジェクトのスケール、 35 // voxelsPerUnitをもとにボリュームのサイズを決める 36 this.meshFilter = this.GetComponent<MeshFilter>(); 37 this.sourceMesh = this.meshFilter.sharedMesh; 38 this.initialScale = this.transform.localScale; 39 var sourceBounds = this.sourceMesh.bounds; 40 var scaledSourceBounds = new Bounds( 41 Vector3.Scale(sourceBounds.center, this.initialScale), 42 Vector3.Scale(sourceBounds.size, this.initialScale)); 43 var textureSize = scaledSourceBounds.size * this.voxelsPerUnit; 44 this.textureSizeInt = new Vector3Int( 45 Mathf.CeilToInt(textureSize.x) + 2, 46 Mathf.CeilToInt(textureSize.y) + 2, 47 Mathf.CeilToInt(textureSize.z) + 2); 48 textureSize = this.textureSizeInt; 49 50 // 本来のメッシュを包むようにボリューム描画用メッシュを作る 51 this.volumeBounds = new Bounds(scaledSourceBounds.center, textureSize / this.voxelsPerUnit); 52 var vMin = this.volumeBounds.min; 53 var vMax = this.volumeBounds.max; 54 this.volumeMesh = new Mesh 55 { 56 name = this.sourceMesh.name + " Volume", 57 vertices = new[] 58 { 59 vMin, 60 new Vector3(vMax.x, vMin.y, vMin.z), 61 new Vector3(vMin.x, vMax.y, vMin.z), 62 new Vector3(vMax.x, vMax.y, vMin.z), 63 new Vector3(vMin.x, vMin.y, vMax.z), 64 new Vector3(vMax.x, vMin.y, vMax.z), 65 new Vector3(vMin.x, vMax.y, vMax.z), 66 vMax 67 }, 68 triangles = new[] 69 { 70 0, 1, 2, 3, 2, 1, 71 0, 2, 4, 6, 4, 2, 72 0, 4, 1, 5, 1, 4, 73 7, 5, 6, 4, 6, 5, 74 7, 3, 5, 1, 5, 3, 75 7, 6, 3, 2, 3, 6 76 } 77 }; 78 this.volumeMesh.RecalculateBounds(); 79 this.meshFilter.sharedMesh = this.volumeMesh; 80 81 // 単純化のため、オブジェクト自体のスケールは1倍とした 82 this.transform.localScale = Vector3.one; 83 84 // ボリュームを作る 85 this.volume = new RenderTexture(this.textureSizeInt.x, this.textureSizeInt.y, 0, RenderTextureFormat.R8) 86 { 87 dimension = TextureDimension.Tex3D, 88 volumeDepth = this.textureSizeInt.z, 89 useMipMap = false 90 }; 91 this.volume.Create(); 92 93 // ボリュームにメッシュの断面を描く 94 var activeTexture = RenderTexture.active; 95 this.sliceMaterial = new Material(this.sliceShader); 96 this.slice0 = new RenderTexture(this.textureSizeInt.x * 4, this.textureSizeInt.y * 4, 24, RenderTextureFormat.R8); 97 this.slice1 = new RenderTexture(this.textureSizeInt.x * 2, this.textureSizeInt.y * 2, 0, RenderTextureFormat.R8); 98 this.slice0.Create(); 99 this.slice1.Create(); 100 var volumeSize = this.volumeBounds.size; 101 this.sliceMatrix = Matrix4x4.TRS( 102 Vector3.zero, 103 Quaternion.identity, 104 new Vector3( 105 this.initialScale.x / volumeSize.x, 106 this.initialScale.y / volumeSize.y, 107 this.initialScale.z / volumeSize.z)) * Matrix4x4.TRS( 108 -sourceBounds.center, 109 Quaternion.identity, 110 Vector3.one); 111 var modelMatrix = this.sliceMatrix; 112 var translationZ = modelMatrix.m23; 113 this.voxelSize = new Vector3(1.0f / textureSize.x, 1.0f / textureSize.y, 1.0f / textureSize.z); 114 this.orthoMatrix = Matrix4x4.Ortho(-0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f); 115 GL.PushMatrix(); 116 GL.LoadIdentity(); 117 GL.LoadProjectionMatrix(this.orthoMatrix); 118 for (var i = 1; i < (this.textureSizeInt.z - 1); i++) 119 { 120 RenderTexture.active = this.slice0; 121 modelMatrix.m23 = translationZ - ((i + 0.5f) * this.voxelSize.z); 122 this.sliceMaterial.SetPass(0); 123 GL.Clear(true, true, Color.clear); 124 Graphics.DrawMeshNow(this.sourceMesh, modelMatrix); 125 Graphics.Blit(this.slice0, this.slice1); 126 Graphics.Blit(this.slice1, this.volume, 0, i); 127 } 128 GL.PopMatrix(); 129 RenderTexture.active = activeTexture; 130 131 // sliceMatrixはゲーム実行中にCarveを行う際にも使用されるが、 132 // Carvee自体のスケールは1倍に修正しているので、それに合わせて 133 // 変更を加えておく 134 this.sliceMatrix = Matrix4x4.TRS( 135 Vector3.zero, 136 Quaternion.identity, 137 new Vector3( 138 1.0f / volumeSize.x, 139 1.0f / volumeSize.y, 140 1.0f / volumeSize.z)) * Matrix4x4.TRS( 141 -this.volumeBounds.center, 142 Quaternion.identity, 143 Vector3.one); 144 145 // ボリューム描画用マテリアルを作り、レンダラーにセットする 146 this.meshRenderer = this.GetComponent<MeshRenderer>(); 147 this.initialMaterial = this.meshRenderer.sharedMaterial; 148 this.volumeMaterial = new Material(this.volumeShader); 149 this.meshRenderer.sharedMaterial = this.volumeMaterial; 150 var volumeToLocal = Matrix4x4.TRS(vMin, Quaternion.identity, volumeSize); 151 var localToVolume = Matrix4x4.Inverse(volumeToLocal); 152 this.volumeMaterial.mainTexture = this.volume; 153 this.volumeMaterial.color = this.color; 154 this.volumeMaterial.SetVector("_VoxelSize", this.voxelSize); 155 this.volumeMaterial.SetVector("_BoundsSize", volumeSize); 156 this.volumeMaterial.SetMatrix("_VolumeToLocal", volumeToLocal); 157 this.volumeMaterial.SetMatrix("_LocalToVolume", localToVolume); 158 } 159 160 // 削り取りを行う時は、削りに使うメッシュとそれのTransformを引数にしてCarveを実行する 161 public void Carve(Mesh carverMesh, Transform carverTransform) 162 { 163 // まず削りメッシュとボリュームの交差範囲を求めて、 164 // 更新するテクスチャの範囲を決める 165 var v = this.carverBoundsVertices; 166 var carverLocalBounds = carverMesh.bounds; 167 v[0] = carverLocalBounds.min; 168 v[7] = carverLocalBounds.max; 169 v[1] = new Vector3(v[7].x, v[0].y, v[0].z); 170 v[2] = new Vector3(v[0].x, v[7].y, v[0].z); 171 v[3] = new Vector3(v[7].x, v[7].y, v[0].z); 172 v[4] = new Vector3(v[0].x, v[0].y, v[7].z); 173 v[5] = new Vector3(v[7].x, v[0].y, v[7].z); 174 v[6] = new Vector3(v[0].x, v[7].y, v[7].z); 175 var min = new Vector3(Mathf.Infinity, Mathf.Infinity, Mathf.Infinity); 176 var max = new Vector3(Mathf.NegativeInfinity, Mathf.NegativeInfinity, Mathf.NegativeInfinity); 177 foreach (var carverLocalPosition in v) 178 { 179 var carveeLocalPosition = 180 this.transform.InverseTransformPoint(carverTransform.TransformPoint(carverLocalPosition)); 181 min = Vector3.Min(min, carveeLocalPosition); 182 max = Vector3.Max(max, carveeLocalPosition); 183 } 184 if (!this.volumeBounds.Intersects(new Bounds((min + max) * 0.5f, max - min))) 185 { 186 return; 187 } 188 var volumeMinZ = this.volumeBounds.min.z; 189 var volumeMaxZ = this.volumeBounds.max.z; 190 int GetIndex(float z, float minZ, float maxZ, int texSize) => Mathf.Clamp( 191 Mathf.RoundToInt((Mathf.InverseLerp(minZ, maxZ, z) * texSize) - 0.5f), 192 0, 193 texSize - 1); 194 var fromIndex = GetIndex(Mathf.Max(volumeMinZ, min.z), volumeMinZ, volumeMaxZ, this.textureSizeInt.z); 195 var toIndex = GetIndex(Mathf.Min(volumeMaxZ, max.z), volumeMinZ, volumeMaxZ, this.textureSizeInt.z); 196 197 // ボリュームをメッシュの形で削り取る 198 var activeTexture = RenderTexture.active; 199 var modelMatrix = this.sliceMatrix * this.transform.worldToLocalMatrix * carverTransform.localToWorldMatrix; 200 var translationZ = modelMatrix.m23; 201 GL.PushMatrix(); 202 GL.LoadIdentity(); 203 GL.LoadProjectionMatrix(this.orthoMatrix); 204 for (var i = fromIndex; i <= toIndex; i++) 205 { 206 RenderTexture.active = this.slice0; 207 modelMatrix.m23 = translationZ - ((i + 0.5f) * this.voxelSize.z); 208 this.sliceMaterial.SetPass(0); 209 GL.Clear(true, true, Color.clear); 210 Graphics.DrawMeshNow(carverMesh, modelMatrix); 211 Graphics.Blit(this.slice0, this.slice1); 212 Graphics.Blit(this.slice0, this.volume, this.sliceMaterial, 1, i); 213 } 214 GL.PopMatrix(); 215 RenderTexture.active = activeTexture; 216 } 217 218 private void OnDestroy() 219 { 220 this.meshRenderer.sharedMaterial = this.initialMaterial; 221 this.meshFilter.sharedMesh = this.sourceMesh; 222 this.transform.localScale = this.initialScale; 223 Destroy(this.sliceMaterial); 224 Destroy(this.volumeMaterial); 225 Destroy(this.volumeMesh); 226 Destroy(this.volume); 227 Destroy(this.slice0); 228 Destroy(this.slice1); 229 } 230}
(パート2へ続く...)
投稿2021/11/22 10:26
総合スコア10811
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/11/22 13:47
2021/11/22 14:34
2021/11/24 06:33
2021/11/24 06:51
2021/11/24 07:03
2021/11/24 10:19 編集
2021/11/26 01:37 編集
2021/11/26 01:34
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。