【问题标题】:How to apply cubemap to inverse of a sphere in Unity 3D如何在 Unity 3D 中将立方体贴图应用于球体的反转
【发布时间】:2016-11-27 21:44:27
【问题描述】:
我正在尝试将 .jpg 纹理应用到球体图元(倒置球体)的内部,以制作用于 VR 的简单 3D 光球,但我遇到了问题。我正在使用统一 5.4
我导入 360 度全景 .jpg 纹理,将其纹理类型设置为立方体贴图,并将其映射设置为经度/纬度(圆柱)。
我创建了一个新材质,我可以在材质的着色器下拉菜单中找到的唯一立方体贴图着色器是天空盒/立方体贴图。我选择了它,但它不会让我将材质分配给球体。我可以将它分配给背景,但我需要它在球体上,我做错了什么?如何将立方体贴图纹理应用到可以应用于倒置球体的材质上?
【问题讨论】:
标签:
unity3d
unity5
virtual-reality
【解决方案1】:
这是一个可能有帮助的着色器
Shader "Flipping Normals" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
}
SubShader {
Tags { "RenderType" = "Opaque" }
Cull Off
CGPROGRAM
#pragma surface surf Lambert vertex:vert
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
float4 color : COLOR;
};
void vert(inout appdata_full v) {
v.normal.xyz = v.normal * -1;
}
void surf (Input IN, inout SurfaceOutput o) {
fixed3 result = tex2D(_MainTex, IN.uv_MainTex);
o.Albedo = result.rgb;
o.Alpha = 1;
}
ENDCG
}
Fallback "Diffuse"
}
【解决方案2】:
着色器代码归功于 Unity 论坛上的用户 Bgolus:
https://forum.unity.com/threads/cube-mapped-sphere-aka-quad-sphere-asset.292860/#post-2708001
该论坛帖子中的着色器代码将允许将立方体贴图渲染成球体和立方体。
我对该代码所做的唯一补充是将Cull Front 添加到着色器中以使网格看起来正常反转,并且您可以使用 Unity 的内置球体作为网格。
Shader "Unlit/InverseCullCubeMapShader"
{
Properties
{
_CubeMap( "Cube Map", Cube ) = "white" {}
}
SubShader
{
Pass
{
Tags { "DisableBatching" = "True" }
Cull Front
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
samplerCUBE _CubeMap;
struct v2f
{
float4 pos : SV_Position;
half3 uv : TEXCOORD0;
};
v2f vert( appdata_img v )
{
v2f o;
o.pos = UnityObjectToClipPos( v.vertex );
o.uv = v.vertex.xyz * half3(-1,1,1); // mirror so cubemap projects as expected
return o;
}
fixed4 frag( v2f i ) : SV_Target
{
return texCUBE( _CubeMap, i.uv );
}
ENDCG
}
}
}
【解决方案3】:
这是一个组件形式的非着色器解决方案,它将为您反转球体的法线,然后您可以简单地应用您提到的任何 equirectangular 纹理。我假设你需要一个球体的内部,这样你就可以与全景图中的实际几何体/区域进行交互(这也是我写这篇文章的原因)。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshFilter))]
public class InvertNormals : BaseView
{
void Start()
{
// Firstly, we grab this object's mesh filter component
MeshFilter filter = gameObject.GetComponent<MeshFilter>();
if (filter != null)
{
// Now that we have the mesh filter component, we can grab the
topology itself...
Mesh skyboxSphereMesh = filter.mesh;
// ...and cache all of its normals into an array of Vector3 values
Vector3[] normals = skyboxSphereMesh.normals;
// We iterate over the array and negate (-x, -y, -z) each vector...
// https://docs.unity3d.com/ScriptReference/Vector3-operator_subtract.html
// ...then re-assign the value
for (int i = 0; i < normals.Length; i++)
normals[i] = -normals[i];
// Re-assign all of our inverted normal values to the mesh
skyboxSphereMesh.normals = normals;
// Now we need to iterate over all of the submeshes, each index
// referring to a list of triangles/trigons, which refer to a set
// of vertices in 3D space
for (int i = 0; i < skyboxSphereMesh.subMeshCount; i++)
{
// Firstly, we cache the triangles...
int[] triangles = skyboxSphereMesh.GetTriangles(i);
// ...then iterate over the values, incrementing our count by 3 each time
for (int j = 0; j < triangles.Length; j += 3)
{
// We're offsetting the index array here and not the actual vertex array,
// this might cause problems with UVs down the line!
int temp = triangles[j + 0]; // Unity Forum magic per-vertex offset
triangles[j + 0] = triangles[j + 1];
triangles[j + 1] = temp;
}
// Re-assign the mesh with a new set of triangle indices at the current submesh
skyboxSphereMesh.SetTriangles(triangles, i);
}
}
}
}