Hello everyone I am trying to make volume rendering raycasting from a series of Dicom slices but I faced a problem that my object is stretched between the front and back faces as follows:
Here is where I created the scene:
export class volumeRender{
constructor(canvas){
this.engine = new Engine(canvas, true);
this.scene = this.CreateScene();
this.engine.runRenderLoop(()=>{
this.scene.render();
})
}
CreateCamera(){
var camera = new ArcRotateCamera("Camera", Math.PI / 4, Math.PI / 4, 4, Vector3.Zero(), this.scene);
// Positions the camera overwriting alpha, beta, radius
// camera.setPosition(new Vector3(2, 2, 20));
camera.speed = 0.3
camera.attachControl();
return camera
}
CreateScene() {
const scene = new Scene(this.engine);
this.camera = this.CreateCamera();
const hemiLight = new HemisphericLight("hemiLight", new Vector3(0,1,0), scene);
hemiLight.intensity = 0.5;
this.backFace = MeshBuilder.CreateBox("backSide", {size: 1, sideOrientation: Mesh.BACKSIDE}, scene);
this.backFace.material = new ShaderMaterial("backFaceShader", scene, "./backFace",
{
attributes: ["position"],
uniforms: [ "view", "projection"],
});
//this will allow the box to only render to the rendertarget
this.backFace.layerMask = 0x10000000;
this.frontFace = MeshBuilder.CreateBox("frontSide", {size: 1, sideOrientation: Mesh.FRONTSIDE}, scene);
this.frontFaceMaterial = new ShaderMaterial("frontFaceShader", scene, "./frontFace",
{
needAlphaBlending: true,
needAlphaTesting: true,
attributes: ["position"],
uniforms: ["world", "view", "projection", "steps", "alphaCorrection"],
samplers: [ "tex", "textureSampler", "transferFunction"],
});
var renderTarget = new RenderTargetTexture(
'render to texture', // name
512, // texture size
scene // the scene
);
scene.customRenderTargets.push(renderTarget);
renderTarget.renderList.push(this.backFace);
var transFuncArray = []
for (let i=0;i<=255;i++){
transFuncArray.push(i)
transFuncArray.push(0)
transFuncArray.push(0)
transFuncArray.push(i)
}
var transferTexture = new RawTexture(new Uint8Array(transFuncArray), 256,1, Engine.TEXTUREFORMAT_RGBA, scene)
this.frontFaceMaterial.setFloat('steps', 256.0);
this.frontFaceMaterial.setFloat('alphaCorrection', 2);
this.frontFaceMaterial.setTexture("tex", renderTarget);
this.frontFaceMaterial.setTexture("transferFunction", transferTexture);
return scene
}
updateTexture(dataArray){
var tempArray = []
dataArray.forEach(slice => {
slice.forEach(element=>{
tempArray.push(element)
})
});
var newTex = new RawTexture3D(new Uint8Array(tempArray), 512,512, 234, Engine.TEXTUREFORMAT_LUMINANCE, this.scene)
this.frontFaceMaterial.setTexture("textureSampler", newTex);
this.frontFace.material = this.frontFaceMaterial;
}
}
and here are my shaders:
Backface vertex shader:
`varying vec3 worldSpaceCoords;
attribute vec3 position;
uniform mat4 view;
uniform mat4 projection;
void main()
{
//Set the world space coordinates of the back faces vertices as output.
worldSpaceCoords = position + vec3(0.5, 0.5, 0.5);
gl_Position = projection * view * vec4( position, 1.0 );
}`
Backface fragment shader:
varying vec3 worldSpaceCoords;
void main()
{
//The fragment's world space coordinates as fragment output.
gl_FragColor = vec4( worldSpaceCoords.x , worldSpaceCoords.y, worldSpaceCoords.z, 1 );
}
Frontface vertex shader:
precision highp float;
varying vec3 worldSpaceCoords;
varying vec4 projectedCoords;
uniform mat4 projection, view, world;
attribute vec3 position;
void main()
{
worldSpaceCoords = (world * vec4(position + vec3(0.5, 0.5,0.5), 1.0 )).xyz;
gl_Position = projection * view * vec4( position, 1.0 );
projectedCoords = gl_Position;
}
Frontface fragment shader:
precision highp float;
precision highp sampler3D;
// varying vec3 vUV;
// varying vec3 positionAttrib;
varying vec4 projectedCoords;
varying vec3 worldSpaceCoords;
uniform sampler2D tex;
uniform sampler2D transferFunction;
uniform sampler3D textureSampler;
uniform float steps;
uniform float alphaCorrection;
const int MAX_STEPS = 887;
void main(void) {
vec2 texc = vec2(
((projectedCoords.x / projectedCoords.w) + 1.0 ) / 2.0,
((projectedCoords.y / projectedCoords.w) + 1.0 ) / 2.0);
vec3 backPos = texture2D(tex, texc).xyz;
vec3 frontPos = worldSpaceCoords;
//Using NearestFilter for rtTexture mostly eliminates bad backPos values at the edges
//of the cube, but there may still be no valid backPos value for the current fragment.
if ((backPos.x == 0.0) && (backPos.y == 0.0))
{
gl_FragColor = vec4(0.0);
return;
}
//The direction from the front position to back position.
vec3 dir = backPos - frontPos;
float rayLength = length(dir);
//Calculate how long to increment in each step.
float delta = 1.0 / steps;
//The increment in each direction for each step.
vec3 deltaDirection = normalize(dir) * delta;
float deltaDirectionLength = length(deltaDirection);
//Start the ray casting from the front position.
vec3 currentPosition = frontPos;
//The color accumulator.
vec4 accumulatedColor = vec4(0.0);
//The alpha value accumulated so far.
float accumulatedAlpha = 0.0;
//How long has the ray travelled so far.
float accumulatedLength = 0.0;
//If we have twice as many samples, we only need ~1/2 the alpha per sample.
//Scaling by 256/10 just happens to give a good value for the alphaCorrection slider.
float alphaScaleFactor = 25.6 * delta;
vec4 colorSample;
vec4 voxelIntensity;
float alphaSample;
//Perform the ray marching iterations
for(int i = 0; i < MAX_STEPS; i++)
{
//Get the voxel intensity value from the 3D texture.
voxelIntensity = texture(textureSampler, currentPosition);
colorSample = texture2D(transferFunction, vec2(voxelIntensity.r, 1.0));
//Allow the alpha correction customization.
alphaSample = colorSample.a * alphaCorrection;
//Applying this effect to both the color and alpha accumulation results in more realistic transparency.
alphaSample *= (1.0 - accumulatedAlpha);
//Scaling alpha by the number of steps makes the final color invariant to the step size.
alphaSample *= alphaScaleFactor;
//Perform the composition.
accumulatedColor += colorSample * alphaSample;
//Store the alpha accumulated so far.
accumulatedAlpha += alphaSample;
//Advance the ray.
currentPosition += deltaDirection;
accumulatedLength += deltaDirectionLength;
//If the length traversed is more than the ray length, or if the alpha accumulated reaches 1.0 then exit.
if(accumulatedLength >= rayLength || accumulatedAlpha >= 1.0 )
break;
}
gl_FragColor = accumulatedColor;
}