279 lines
6.9 KiB
Plaintext
279 lines
6.9 KiB
Plaintext
|
shader_type spatial;
|
||
|
render_mode blend_mix, depth_prepass_alpha, cull_back, unshaded;
|
||
|
uniform vec4 albedo : source_color = vec4(1, 1, 1, 1);
|
||
|
uniform float specular = 0.5f;
|
||
|
uniform float metallic = 0.0f;
|
||
|
uniform float roughness : hint_range(0, 1) = 1.0f;
|
||
|
|
||
|
uniform sampler2D imposterTextureAlbedo : source_color;
|
||
|
uniform vec2 imposterFrames = vec2(16.0f, 16.0f);
|
||
|
uniform vec3 positionOffset = vec3(0.0f);
|
||
|
uniform bool isFullSphere = true;
|
||
|
uniform float alpha_clamp = 0.5f;
|
||
|
uniform float scale = 1.0f;
|
||
|
uniform float depth_scale : hint_range(0, 1) = 0.0f;
|
||
|
uniform float aabb_max = 1.0;
|
||
|
|
||
|
varying vec2 uv_frame1;
|
||
|
varying vec2 xy_frame1;
|
||
|
varying flat vec2 frame1;
|
||
|
|
||
|
varying vec4 quad_blend_weights;
|
||
|
|
||
|
vec2 VecToSphereOct(vec3 pivotToCamera)
|
||
|
{
|
||
|
vec3 octant = sign(pivotToCamera);
|
||
|
|
||
|
// |x| + |y| + |z| = 1
|
||
|
float sum = dot(pivotToCamera, octant);
|
||
|
vec3 octahedron = pivotToCamera / sum;
|
||
|
|
||
|
if (octahedron.y < 0.0f)
|
||
|
{
|
||
|
vec3 absolute = abs(octahedron);
|
||
|
octahedron.xz = octant.xz * vec2(1.0f - absolute.z, 1.0f - absolute.x);
|
||
|
}
|
||
|
return octahedron.xz;
|
||
|
}
|
||
|
|
||
|
vec2 VecToHemiSphereOct(vec3 pivotToCamera)
|
||
|
{
|
||
|
pivotToCamera.y = max(pivotToCamera.y, 0.001);
|
||
|
pivotToCamera = normalize(pivotToCamera);
|
||
|
vec3 octant = sign(pivotToCamera);
|
||
|
|
||
|
// |x| + |y| + |z| = 1
|
||
|
float sum = dot(pivotToCamera, octant);
|
||
|
vec3 octahedron = pivotToCamera / sum;
|
||
|
|
||
|
return vec2(
|
||
|
octahedron.x + octahedron.z,
|
||
|
octahedron.z - octahedron.x);
|
||
|
}
|
||
|
|
||
|
vec2 VectorToGrid(vec3 vec)
|
||
|
{
|
||
|
if (isFullSphere)
|
||
|
{
|
||
|
return VecToSphereOct(vec);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return VecToHemiSphereOct(vec);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//for sphere
|
||
|
vec3 OctaSphereEnc(vec2 coord)
|
||
|
{
|
||
|
coord = (coord - 0.5) * 2.0;
|
||
|
vec3 position = vec3(coord.x, 0.0f, coord.y);
|
||
|
vec2 absolute = abs(position.xz);
|
||
|
position.y = 1.0f - absolute.x - absolute.y;
|
||
|
|
||
|
if (position.y < 0.0f)
|
||
|
{
|
||
|
position.xz = sign(position.xz) * vec2(1.0f - absolute.y, 1.0f - absolute.x);
|
||
|
}
|
||
|
|
||
|
return position;
|
||
|
}
|
||
|
|
||
|
//for hemisphere
|
||
|
vec3 OctaHemiSphereEnc(vec2 coord)
|
||
|
{
|
||
|
|
||
|
vec3 position = vec3(coord.x - coord.y, 0.0f, -1.0 + coord.x + coord.y);
|
||
|
vec2 absolute = abs(position.xz);
|
||
|
position.y = 1.0f - absolute.x - absolute.y;
|
||
|
return position;
|
||
|
}
|
||
|
|
||
|
vec3 GridToVector(vec2 coord)
|
||
|
{
|
||
|
if (isFullSphere)
|
||
|
{
|
||
|
return OctaSphereEnc(coord);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return OctaHemiSphereEnc(coord);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
vec3 FrameXYToRay(vec2 frame, vec2 frameCountMinusOne)
|
||
|
{
|
||
|
//divide frame x y by framecount minus one to get 0-1
|
||
|
vec2 f = (frame.xy/ frameCountMinusOne);
|
||
|
//bias and scale to -1 to 1
|
||
|
|
||
|
vec3 vec = GridToVector(f);
|
||
|
vec = normalize(vec);
|
||
|
return vec;
|
||
|
}
|
||
|
|
||
|
vec3 SpriteProjection(vec3 pivotToCameraRayLocal, vec2 size, vec2 loc_uv)
|
||
|
{
|
||
|
vec3 z = normalize(pivotToCameraRayLocal);
|
||
|
vec3 x, y;
|
||
|
vec3 up = vec3(0,1,0);
|
||
|
//cross product doesnt work if we look directly from bottom
|
||
|
if (abs(z.y) > 0.999f)
|
||
|
{
|
||
|
up = vec3(0,0,-1);
|
||
|
}
|
||
|
x = normalize(cross(up, z));
|
||
|
y = normalize(cross(x, z));
|
||
|
|
||
|
loc_uv -= vec2(0.5,0.5);
|
||
|
vec2 uv = (loc_uv) * 2.0; //-1 to 1
|
||
|
|
||
|
vec3 newX = x * uv.x;
|
||
|
vec3 newY = y * uv.y;
|
||
|
|
||
|
vec2 vecSize = size * 0.5;
|
||
|
|
||
|
newX *= vecSize.x;
|
||
|
newY *= vecSize.y;
|
||
|
|
||
|
return newX + newY;
|
||
|
}
|
||
|
|
||
|
vec4 quadBlendWieghts(vec2 coords)
|
||
|
{
|
||
|
vec4 res;
|
||
|
/* 0 0 0
|
||
|
0 0 0
|
||
|
1 0 0 */
|
||
|
res.x = min(1.0f - coords.x, 1.0f - coords.y);
|
||
|
/* 1 0 0
|
||
|
0 0 0
|
||
|
0 0 1 */
|
||
|
res.y = abs(coords.x - coords.y);
|
||
|
/* 0 0 1
|
||
|
0 0 0
|
||
|
0 0 0 */
|
||
|
res.z = min(coords.x, coords.y);
|
||
|
/* 0 0 0
|
||
|
0 0 1
|
||
|
0 1 1 */
|
||
|
res.w = ceil(coords.x - coords.y);
|
||
|
//res.xyz /= (res.x + res.y + res.z);
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
|
||
|
//this function works well in orthogonal projection. It works okeyish with further distances of perspective projection
|
||
|
vec2 virtualPlaneUV(vec3 plane_normal,vec3 plane_x, vec3 plane_y, vec3 pivotToCameraRay, vec3 vertexToCameraRay, float size)
|
||
|
{
|
||
|
plane_normal = normalize(plane_normal);
|
||
|
plane_x = normalize(plane_x);
|
||
|
plane_y = normalize(plane_y);
|
||
|
|
||
|
float projectedNormalRayLength = dot(plane_normal, pivotToCameraRay);
|
||
|
float projectedVertexRayLength = dot(plane_normal, vertexToCameraRay);
|
||
|
float offsetLength = projectedNormalRayLength/projectedVertexRayLength;
|
||
|
vec3 offsetVector = vertexToCameraRay * offsetLength - pivotToCameraRay;
|
||
|
|
||
|
vec2 duv = vec2(
|
||
|
dot(plane_x , offsetVector),
|
||
|
dot(plane_y, offsetVector)
|
||
|
);
|
||
|
|
||
|
//we are in space -1 to 1
|
||
|
duv /= 2.0 * size;
|
||
|
duv += 0.5;
|
||
|
return duv;
|
||
|
}
|
||
|
|
||
|
void calcuateXYbasis(vec3 plane_normal, out vec3 plane_x, out vec3 plane_y)
|
||
|
{
|
||
|
vec3 up = vec3(0,1,0);
|
||
|
//cross product doesnt work if we look directly from bottom
|
||
|
if (abs(plane_normal.y) > 0.999f)
|
||
|
{
|
||
|
up = vec3(0,0,1);
|
||
|
}
|
||
|
plane_x = normalize(cross(plane_normal, up));
|
||
|
plane_y = normalize(cross(plane_x, plane_normal));
|
||
|
}
|
||
|
|
||
|
vec3 projectOnPlaneBasis(vec3 ray, vec3 plane_normal, vec3 plane_x, vec3 plane_y)
|
||
|
{
|
||
|
//reproject plane normal onto planeXY basos
|
||
|
return normalize(vec3(
|
||
|
dot(plane_x,ray),
|
||
|
dot(plane_y,ray),
|
||
|
dot(plane_normal,ray)
|
||
|
));
|
||
|
}
|
||
|
|
||
|
void vertex()
|
||
|
{
|
||
|
vec2 framesMinusOne = imposterFrames - vec2(1);
|
||
|
vec3 cameraPos_WS = (INV_VIEW_MATRIX * vec4(vec3(0), 1.0)).xyz;
|
||
|
vec3 cameraPos_OS = (inverse(MODEL_MATRIX) * vec4(cameraPos_WS, 1.0)).xyz;
|
||
|
|
||
|
//TODO: check if this is correct. We are using orho projected images, so
|
||
|
// camera far away
|
||
|
vec3 pivotToCameraRay = (cameraPos_OS) * 10.0;
|
||
|
vec3 pivotToCameraDir = normalize(cameraPos_OS);
|
||
|
|
||
|
vec2 grid = VectorToGrid(pivotToCameraDir);
|
||
|
//bias and scale to 0 to 1
|
||
|
grid = clamp((grid + 1.0) * 0.5, vec2(0, 0), vec2(1, 1));
|
||
|
grid *= framesMinusOne;
|
||
|
grid = clamp(grid, vec2(0), vec2(framesMinusOne));
|
||
|
vec2 gridFloor = min(floor(grid), framesMinusOne);
|
||
|
vec2 gridFract = fract(grid);
|
||
|
|
||
|
//radius * 2
|
||
|
vec2 size = vec2(2.0) * scale;
|
||
|
vec3 projected = SpriteProjection(pivotToCameraDir, size, UV);
|
||
|
vec3 vertexToCameraRay = (pivotToCameraRay - (projected));
|
||
|
vec3 vertexToCameraDir = normalize(vertexToCameraRay);
|
||
|
|
||
|
frame1 = gridFloor;
|
||
|
quad_blend_weights = quadBlendWieghts(gridFract);
|
||
|
//convert frame coordinate to octahedron direction
|
||
|
vec3 projectedQuadADir = FrameXYToRay(frame1, framesMinusOne);
|
||
|
|
||
|
//calcute virtual planes projections
|
||
|
vec3 plane_x1, plane_y1;
|
||
|
calcuateXYbasis(projectedQuadADir, plane_x1, plane_y1);
|
||
|
uv_frame1 = virtualPlaneUV(projectedQuadADir, plane_x1, plane_y1, pivotToCameraRay, vertexToCameraRay, scale);
|
||
|
xy_frame1 = projectOnPlaneBasis(-vertexToCameraDir, projectedQuadADir, plane_x1, plane_y1).xy;
|
||
|
|
||
|
|
||
|
|
||
|
//to fragment shader
|
||
|
VERTEX.xyz = projected + positionOffset;
|
||
|
VERTEX.xyz +=pivotToCameraDir* aabb_max;
|
||
|
vec3 v0 = abs(NORMAL.y) < 0.999 ? vec3(0.0, 1.0, 0.0) : vec3(0.0, 0.0, 1.0);
|
||
|
|
||
|
NORMAL = normalize(pivotToCameraDir);
|
||
|
TANGENT= normalize(cross(NORMAL,v0));
|
||
|
BINORMAL = normalize(cross(TANGENT,NORMAL));
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
vec2 recalculateUV(vec2 uv_f, vec2 frame, vec2 xy_f, vec2 frame_size)
|
||
|
{
|
||
|
//clamp for parallax sampling
|
||
|
uv_f = clamp(uv_f, vec2(0), vec2(1));
|
||
|
return frame_size * (frame + uv_f);
|
||
|
}
|
||
|
|
||
|
void fragment()
|
||
|
{
|
||
|
vec2 quad_size = vec2(1.0f) / imposterFrames;
|
||
|
vec2 uv_f1 = recalculateUV(uv_frame1, frame1, xy_frame1, quad_size);
|
||
|
vec4 baseTex = textureLod(imposterTextureAlbedo, uv_f1, 0.0f);
|
||
|
|
||
|
ALBEDO = baseTex.rgb;
|
||
|
ALPHA = float(baseTex.a>alpha_clamp);
|
||
|
ALPHA_SCISSOR_THRESHOLD = 0.5;
|
||
|
ROUGHNESS = 1.0;
|
||
|
}
|