Hi guys, I'm new to graphics programming, recently I'm trying to implement ray marching on 3d textures, I want to use it in my voxel engine. Here is the code.
Vertex Shader:
#version 300 es
precision highp float;
layout(location=0) in vec3 aVertexPosition;
layout(location=1) in vec4 aVertexColor;
uniform mat4 uModelMatrix;
uniform mat4 uViewMatrix;
uniform mat4 uProjectionMatrix;
uniform vec3 eye_pos;
out lowp vec4 vColor;
out vec3 ray;
flat out vec3 eye;
void main(void) {
gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * vec4(aVertexPosition, 1.0);
vColor = aVertexColor;
ray = aVertexPosition - eye_pos;
eye = eye_pos - vec3(-32.0, -32.0, -32.0);
}
Fragment Shader:
#version 300 es
precision highp float;
in lowp vec4 vColor;
in vec3 ray;
flat in vec3 eye;
uniform highp sampler3D volume;
out vec4 fragColor;
int FRONT = 1, BACK = 2, LEFT = 3, RIGHT = 4, TOP = 5, BOTTOM = 6, NONE = 0;
float minLength = -1.0;
bool hit(inout int face, int plane, float planeCoord) {
vec3 ray_dir = normalize(ray);
if (plane == 1) {
float mag = (planeCoord - eye.x) / ray_dir.x;
if (mag < 0.1) {
return false;
}
vec3 hitPoint = eye + ray_dir * mag;
hitPoint.x = planeCoord;
if (hitPoint.y > 64.0 || hitPoint.y < 0.0 || hitPoint.z > 64.0 || hitPoint.z < 0.0) {
return false;
}
if (minLength < 0.0 || mag < minLength) {
if (ray_dir.x < 0.0 && texture(volume, vec3((hitPoint.x - 1.0) / 64.0, floor(hitPoint.y) / 64.0, floor(hitPoint.z) / 64.0)).w == 1.0) {
face = RIGHT;
minLength = mag;
return true;
}
if (ray_dir.x > 0.0 && texture(volume, vec3(hitPoint.x / 64.0, floor(hitPoint.y) / 64.0, floor(hitPoint.z) / 64.0)).w == 1.0) {
face = LEFT;
minLength = mag;
return true;
}
} else {
return true;
}
}
if (plane == 2) {
float mag = (planeCoord - eye.y) / ray_dir.y;
if (mag < 0.1) {
return false;
}
vec3 hitPoint = eye + ray_dir * mag;
hitPoint.y = planeCoord;
if (hitPoint.x > 64.0 || hitPoint.x < 0.0 || hitPoint.z > 64.0 || hitPoint.z < 0.0) {
return false;
}
if (minLength < 0.0 || mag < minLength) {
if (ray_dir.y < 0.0 && texture(volume, vec3(floor(hitPoint.x) / 64.0, (hitPoint.y - 1.0) / 64.0, floor(hitPoint.z) / 64.0)).w == 1.0) {
face = FRONT;
minLength = mag;
return true;
}
if (ray_dir.y > 0.0 && texture(volume, vec3(floor(hitPoint.x) / 64.0, hitPoint.y / 64.0, floor(hitPoint.z) / 64.0)).w == 1.0) {
face = BACK;
minLength = mag;
return true;
}
} else {
return true;
}
}
if (plane == 3) {
float mag = (planeCoord - eye.z) / ray_dir.z;
if (mag < 0.1) {
return false;
}
vec3 hitPoint = eye + ray_dir * mag;
hitPoint.z = planeCoord;
if (hitPoint.x > 64.0 || hitPoint.x < 0.0 || hitPoint.y > 64.0 || hitPoint.y < 0.0) {
return false;
}
if (minLength < 0.0 || mag < minLength) {
if (ray_dir.z < 0.0 && texture(volume, vec3(floor(hitPoint.x) / 64.0, floor(hitPoint.y) / 64.0, (hitPoint.z - 1.0) / 64.0)).w == 1.0) {
face = TOP;
minLength = mag;
return true;
}
if (ray_dir.z > 0.0 && texture(volume, vec3(floor(hitPoint.x) / 64.0, floor(hitPoint.y) / 64.0, hitPoint.z / 64.0)).w == 1.0) {
face = BOTTOM;
minLength = mag;
return true;
}
} else {
return true;
}
}
return false;
}
void main(void) {
int face = 0;
if (ray.x > 0.0) {
for (float i = 0.0; i < 64.0; ++i) {
if (hit(face, 1, i)) {
break;
}
}
} else {
for (float i = 64.0; i > 0.0; --i) {
if (hit(face, 1, i)) {
break;
}
}
}
if (ray.y > 0.0) {
for (float i = 0.0; i < 64.0; ++i) {
if (hit(face, 2, i)) {
break;
}
}
} else {
for (float i = 64.0; i > 0.0; --i) {
if (hit(face, 2, i)) {
break;
}
}
}
if (ray.z > 0.0) {
for (float i = 0.0; i < 64.0; ++i) {
if (hit(face, 3, i)) {
break;
}
}
} else {
for (float i = 64.0; i > 0.0; --i) {
if (hit(face, 3, i)) {
break;
}
}
}
if (face == LEFT) {
fragColor = vec4(1.0, 0.0, 0.0, 1.0);
return;
}
if (face == RIGHT) {
fragColor = vec4(0.0, 1.0, 0.0, 1.0);
return;
}
if (face == TOP) {
fragColor = vec4(0.0, 0.0, 1.0, 1.0);
return;
}
if (face == BOTTOM) {
fragColor = vec4(1.0, 1.0, 0.0, 1.0);
return;
}
if (face == FRONT) {
fragColor = vec4(0.0, 1.0, 1.0, 1.0);
return;
}
if (face == BACK) {
fragColor = vec4(1.0, 0.0, 1.0, 1.0);
return;
}
discard;
}
3D Texture:
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_3D, texture);
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
// Assuming data is a typed array containing texture data
const width = 64; // Replace with your desired width
const height = 64; // Replace with your desired height
const depth = 64; // Replace with your desired depth
const data = new Uint8Array(width * height * depth * 4);
for (let z = 0; z < depth; z++) {
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
// Calculate texture value based on procedural generation algorithms
const value = Math.round(Math.random());
const offset = (z * width * height + y * width + x) * 4;
data[offset] = 0; // Red channel
data[offset + 1] = 0; // Green channel
data[offset + 2] = 0; // Blue channel
data[offset + 3] = 255 * value; // Alpha channel (set to 255 for opaque texture)
}
}
}
gl.texImage3D(
gl.TEXTURE_3D,
0, // level
gl.RGBA, // internalFormat
width,
height,
depth,
0, // border
gl.RGBA, // format
gl.UNSIGNED_BYTE, // type
data // your texture data (e.g., a typed array)
);
Well it works, but does not work well, the FPS is low. Basically I'm just traversing through each plane in the volume texture towards the 3 axis and check whether the ray hit anything, is there anything wrong with my algorithm or my code? Any advice is appreciated, thanks!