Emil Persson’s Cube Map based interior mapping
Just does the interior mapping with no additional effects (exterior walls, random lighting, etc.). Does do a random flip / rotation which effectively recreates the basic look of the cube map array version, but not the actual functionality as Emil was lazy and just used the same 6 textures over and over to create 8 “unique” cube maps.
// Adapted to Unity from http://www.humus.name/index.php?page=3D&ID=80
Shader "Custom/InteriorMapping - Cubemap"
{
Properties
{
_RoomCube ("Room Cube Map", Cube) = "white" {}
[Toggle(_USEOBJECTSPACE)] _UseObjectSpace ("Use Object Space", Float) = 0.0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma shader_feature _USEOBJECTSPACE
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
float4 tangent : TANGENT;
};
struct v2f
{
float4 pos : SV_POSITION;
#ifdef _USEOBJECTSPACE
float3 uvw : TEXCOORD0;
#else
float2 uv : TEXCOORD0;
#endif
float3 viewDir : TEXCOORD1;
};
samplerCUBE _RoomCube;
float4 _RoomCube_ST;
// psuedo random
float3 rand3(float co){
return frac(sin(co * float3(12.9898,78.233,43.2316)) * 43758.5453);
}
v2f vert (appdata v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
#ifdef _USEOBJECTSPACE
// slight scaling adjustment to work around "noisy wall" when frac() returns a 0 on surface
o.uvw = v.vertex * _RoomCube_ST.xyx * 0.999 + _RoomCube_ST.zwz;
// get object space camera vector
float4 objCam = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1.0));
o.viewDir = v.vertex.xyz - objCam.xyz;
// adjust for tiling
o.viewDir *= _RoomCube_ST.xyx;
#else
// uvs
o.uv = TRANSFORM_TEX(v.uv, _RoomCube);
// get tangent space camera vector
float4 objCam = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1.0));
float3 viewDir = v.vertex.xyz - objCam.xyz;
float tangentSign = v.tangent.w * unity_WorldTransformParams.w;
float3 bitangent = cross(v.normal.xyz, v.tangent.xyz) * tangentSign;
o.viewDir = float3(
dot(viewDir, v.tangent.xyz),
dot(viewDir, bitangent),
dot(viewDir, v.normal)
);
// adjust for tiling
o.viewDir *= _RoomCube_ST.xyx;
#endif
return o;
}
fixed4 frag (v2f i) : SV_Target
{
#ifdef _USEOBJECTSPACE
// room uvws
float3 roomUVW = frac(i.uvw);
// raytrace box from object view dir
float3 pos = roomUVW * 2.0 - 1.0;
float3 id = 1.0 / i.viewDir;
float3 k = abs(id) - pos * id;
float kMin = min(min(k.x, k.y), k.z);
pos += kMin * i.viewDir;
// randomly flip & rotate cube map for some variety
float3 flooredUV = floor(i.uvw);
float3 r = rand3(flooredUV.x + flooredUV.y + flooredUV.z);
float2 cubeflip = floor(r.xy * 2.0) * 2.0 - 1.0;
pos.xz *= cubeflip;
pos.xz = r.z > 0.5 ? pos.xz : pos.zx;
#else
// room uvs
float2 roomUV = frac(i.uv);
// raytrace box from tangent view dir
float3 pos = float3(roomUV * 2.0 - 1.0, 1.0);
float3 id = 1.0 / i.viewDir;
float3 k = abs(id) - pos * id;
float kMin = min(min(k.x, k.y), k.z);
pos += kMin * i.viewDir;
// randomly flip & rotate cube map for some variety
float2 flooredUV = floor(i.uv);
float3 r = rand3(flooredUV.x + 1.0 + flooredUV.y * (flooredUV.x + 1));
float2 cubeflip = floor(r.xy * 2.0) * 2.0 - 1.0;
pos.xz *= cubeflip;
pos.xz = r.z > 0.5 ? pos.xz : pos.zx;
#endif
// sample room cube map
fixed4 room = texCUBE(_RoomCube, pos.xyz);
return fixed4(room.rgb, 1.0);
}
ENDCG
}
}
}
Test cubemap:
Andrew Willmott’s SimCity 5 style texture atlas interior mapping
Started with something like the above cube map based interior mapping shader, but uses a single 2D texture atlas with variable room depth. Room depth is stored in the alpha channel of the atlas texture. For a cube shaped room the back wall should be 1/2 the size of the visible tile, and a value of 128 in the alpha channel. If you render these out you want to use a camera with a horizontal FOV of 53.13 degrees a room width back from the opening.
Shader "Custom/InteriorMapping - 2D Atlas"
{
Properties
{
_RoomTex ("Room Atlas RGB (A - back wall fraction)", 2D) = "white" {}
_Rooms ("Room Atlas Rows&Cols (XY)", Vector) = (1,1,0,0)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
float4 tangent : TANGENT;
};
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 tangentViewDir : TEXCOORD1;
};
sampler2D _RoomTex;
float4 _RoomTex_ST;
float2 _Rooms;
v2f vert (appdata v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _RoomTex);
// get tangent space camera vector
float4 objCam = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1.0));
float3 viewDir = v.vertex.xyz - objCam.xyz;
float tangentSign = v.tangent.w * unity_WorldTransformParams.w;
float3 bitangent = cross(v.normal.xyz, v.tangent.xyz) * tangentSign;
o.tangentViewDir = float3(
dot(viewDir, v.tangent.xyz),
dot(viewDir, bitangent),
dot(viewDir, v.normal)
);
o.tangentViewDir *= _RoomTex_ST.xyx;
return o;
}
// psuedo random
float2 rand2(float co){
return frac(sin(co * float2(12.9898,78.233)) * 43758.5453);
}
fixed4 frag (v2f i) : SV_Target
{
// room uvs
float2 roomUV = frac(i.uv);
float2 roomIndexUV = floor(i.uv);
// randomize the room
float2 n = floor(rand2(roomIndexUV.x + roomIndexUV.y * (roomIndexUV.x + 1)) * _Rooms.xy);
roomIndexUV += n;
// get room depth from room atlas alpha
fixed farFrac = tex2D(_RoomTex, (roomIndexUV + 0.5) / _Rooms).a;
float depthScale = 1.0 / (1.0 - farFrac) - 1.0;
// raytrace box from view dir
float3 pos = float3(roomUV * 2 - 1, -1);
// pos.xy *= 1.05;
i.tangentViewDir.z *= -depthScale;
float3 id = 1.0 / i.tangentViewDir;
float3 k = abs(id) - pos * id;
float kMin = min(min(k.x, k.y), k.z);
pos += kMin * i.tangentViewDir;
// 0.0 - 1.0 room depth
float interp = pos.z * 0.5 + 0.5;
// account for perspective in "room" textures
// assumes camera with an fov of 53.13 degrees (atan(0.5))
float realZ = saturate(interp) / depthScale + 1;
interp = 1.0 - (1.0 / realZ);
interp *= depthScale + 1.0;
// iterpolate from wall back to near wall
float2 interiorUV = pos.xy * lerp(1.0, farFrac, interp);
interiorUV = interiorUV * 0.5 + 0.5;
// sample room atlas texture
fixed4 room = tex2D(_RoomTex, (roomIndexUV + interiorUV.xy) / _Rooms);
return fixed4(room.rgb, 1.0);
}
ENDCG
}
}
}
Single tile version for basic debugging. Note it looks faded out here because it’s alpha value is 50%!
A 4x2 atlas with varying room lengths (1/2 room, cube, 2x length, 4x length) and lit / unlit variants. Again looks funny because of the alpha.
Both of these could be made better in several ways, especially with better art.