使用 Delphi GLScene 进行 GLSL 光线投射
我使用 GLScene Texture3D 演示的修改版本,探索如何使用 GLScene 着色器。我可以让着色器工作,但不能让光线投射算法(单通道)工作。 在FormCreate中,指定了一个单位立方体:
//unit cube, for raycasting
FVertexArray[0] := Vector3fMake(-0.5, -0.5, -0.5);
FVertexArray[1] := Vector3fMake( 0.5, -0.5, -0.5);
FVertexArray[2] := Vector3fMake( 0.5, 0.5, -0.5);
FVertexArray[3] := Vector3fMake(-0.5, 0.5, -0.5);
FVertexArray[4] := Vector3fMake(-0.5, -0.5, 0.5);
FVertexArray[5] := Vector3fMake( 0.5, -0.5, 0.5);
FVertexArray[6] := Vector3fMake( 0.5, 0.5, 0.5);
FVertexArray[7] := Vector3fMake(-0.5, 0.5, 0.5);
该项目使用GLScene的TGLDirectOpenGL:
procedure TForm1.GLDirectOpenGLRender(Sender: TObject; var rci: TGLRenderContextInfo);
Var
i : Integer;
step : Single;
z : Single;
mat : TMatrix;
v : TVector;
vx, vy, vz : TAffineVector;
begin
if M_3D_Texture.Handle = 0 then
begin
//Assert (GL_EXT_texture3D, 'Graphic card does not support 3D textures.');
M_3D_Texture.AllocateHandle;
gl.BindTexture (GL_TEXTURE_3D, M_3D_Texture.Handle);
gl.TexParameterf (GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP);
gl.TexParameterf (GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP);
gl.TexParameterf (GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP);
gl.TexParameteri (GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gl.TexParameteri (GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl.TexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); //GL_REPLACE);
gl.TexImage3D (GL_TEXTURE_3D, 0, GL_RGBA8, M_Output_Texture_3D.X_Size, M_Output_Texture_3D.Y_Size, M_Output_Texture_3D.Z_Size, 0, M_Output_Texture_3D.Data_Type, GL_UNSIGNED_BYTE, PChar (M_Output_Texture_3D.Data));
end;
If M_Refresh = TRUE Then
begin { then }
M_Refresh := FALSE;
Calculate_Transfer_function;
gl.TexSubImage3D (GL_TEXTURE_3D, 0, 0, 0, 0, M_Output_Texture_3D.X_Size, M_Output_Texture_3D.Y_Size, M_Output_Texture_3D.Z_Size, M_Output_Texture_3D.Data_Type, GL_UNSIGNED_BYTE, PChar (M_Output_Texture_3D.Data));
end; { then }
//--- added for glsl test
if not initDGL then
begin
if Assigned(ProgHnd) then
begin
ProgHnd.Free;
end;
ProgHnd := TGLProgramHandle.CreateAndAllocate;
ProgHnd.AddShader(TGLVertexShaderHandle,
LoadAnsiStringFromFile('.\vert.glsl'));
if not Raycasting_CB.Checked then
ProgHnd.AddShader(TGLFragmentShaderHandle,
LoadAnsiStringFromFile('.\slicing.frag.glsl'))
else
ProgHnd.AddShader(TGLFragmentShaderHandle,
LoadAnsiStringFromFile('.\\raycast.frag.glsl'));
if not ProgHnd.LinkProgram then
raise Exception.Create(ProgHnd.InfoLog);
initDGL := True;
end;
//---
gl.PushAttrib (GL_ENABLE_BIT);
gl.PushMatrix;
gl.Enable (GL_BLEND);
gl.BlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// glDisable (GL_CULL_FACE);
// glDisable (GL_LIGHTING);
//--- added for glsl test and raycasting
if initDGL then
begin
ProgHnd.UseProgramObject;
ProgHnd.UniformTextureHandle['avoxels', 0, ttTexture3D] := M_3D_Texture.Handle;
if Raycasting_CB.Checked then
begin
//camera position in cube coordinates
ProgHnd.Uniform3f['acamera'] := GLCube1.AbsoluteToLocal(GLCamera.AbsoluteAffinePosition);
with M_Output_Texture_3D do
begin
ProgHnd.Uniform3f['astepsize'] := AffineVectorMake(1/X_Size, 1/Y_Size, 1/Z_Size);
end;
end;
if not ProgHnd.ValidateProgram then
raise Exception.Create(ProgHnd.InfoLog);
end;
gl.TexGenf (GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
gl.TexGenf (GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
gl.TexGenf (GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
SetVector (v, XVector, 0.5);
gl.TexGenfv (GL_S, GL_OBJECT_PLANE, @v);
SetVector (v, YVector, 0.5);
gl.TexGenfv (GL_T, GL_OBJECT_PLANE, @v);
SetVector (v, ZVector, 0.5);
gl.TexGenfv (GL_R, GL_OBJECT_PLANE, @v);
gl.Enable (GL_TEXTURE_GEN_S);
gl.Enable (GL_TEXTURE_GEN_T);
gl.Enable (GL_TEXTURE_GEN_R);
gl.BindTexture (GL_TEXTURE_3D, M_3D_Texture.Handle);
gl.Enable (GL_TEXTURE_3D);
gl.GetFloatv (GL_MODELVIEW_MATRIX, @mat);
if not Raycasting_CB.Checked then
begin
//slicing, works ok
(*
glClipPlane(GL_CLIP_PLANE0, @clip0);
glClipPlane(GL_CLIP_PLANE1, @clip1);
glClipPlane(GL_CLIP_PLANE2, @clip2);
glClipPlane(GL_CLIP_PLANE3, @clip3);
glClipPlane(GL_CLIP_PLANE4, @clip4);
glClipPlane(GL_CLIP_PLANE5, @clip5);
glEnable(GL_CLIP_PLANE0);
glEnable(GL_CLIP_PLANE1);
glEnable(GL_CLIP_PLANE2);
glEnable(GL_CLIP_PLANE3);
glEnable(GL_CLIP_PLANE4);
glEnable(GL_CLIP_PLANE5);
*)
vx.X := mat.X.X;
vy.X := mat.X.Y;
vz.X := mat.X.Z;
vx.Y := mat.Y.X;
vy.Y := mat.Y.Y;
vz.Y := mat.Y.Z;
vx.Z := mat.Z.X;
vy.Z := mat.Z.Y;
vz.Z := mat.Z.Z;
ScaleVector (vx, DIAGONAL_LENGTH * 0.5 / VectorLength (vx));
ScaleVector (vy, DIAGONAL_LENGTH * 0.5 / VectorLength (vy));
ScaleVector (vz, DIAGONAL_LENGTH * 0.5 / VectorLength (vz));
step := DIAGONAL_LENGTH / Projection_N_TB.Position;
z := -DIAGONAL_LENGTH / 2;
gl.begin_ (GL_QUADS);
For i := 0 To Projection_N_TB.Position - 1 Do
begin
gl.Color4f (1.0, 1.0, 1.0, 1.0);
gl.Normal3f (-GLCamera.AbsoluteVectorToTarget.X, -GLCamera.AbsoluteVectorToTarget.Y, -GLCamera.AbsoluteVectorToTarget.Z);
gl.Vertex3f (vx.X + vy.X + vz.X * z, vx.Y + vy.Y + vz.Y * z, vx.Z + vy.Z + vz.Z * z);
gl.Vertex3f (-vx.X + vy.X + vz.X * z, -vx.Y + vy.Y + vz.Y * z, -vx.Z + vy.Z + vz.Z * z);
gl.Vertex3f (-vx.X - vy.X + vz.X * z, -vx.Y - vy.Y + vz.Y * z, -vx.Z - vy.Z + vz.Z * z);
gl.Vertex3f (vx.X - vy.X + vz.X * z, vx.Y - vy.Y + vz.Y * z, vx.Z - vy.Z + vz.Z * z);
z := z + step;
end;
gl.End_;
end
else
begin
//for raycasting, the idea is to draw a cube, and let de fragment shader do its work
gl.begin_ (GL_QUADS);
//xy, pos z
gl.Vertex3f (FVertexArray[4].X, FVertexArray[4].Y, FVertexArray[4].Z);
gl.Vertex3f (FVertexArray[5].X, FVertexArray[5].Y, FVertexArray[5].Z);
gl.Vertex3f (FVertexArray[6].X, FVertexArray[6].Y, FVertexArray[6].Z);
gl.Vertex3f (FVertexArray[7].X, FVertexArray[7].Y, FVertexArray[7].Z);
//xz, neg y
gl.Vertex3f (FVertexArray[4].X, FVertexArray[4].Y, FVertexArray[4].Z);
gl.Vertex3f (FVertexArray[0].X, FVertexArray[0].Y, FVertexArray[0].Z);
gl.Vertex3f (FVertexArray[1].X, FVertexArray[1].Y, FVertexArray[1].Z);
gl.Vertex3f (FVertexArray[5].X, FVertexArray[5].Y, FVertexArray[5].Z);
//xz, pos y
gl.Vertex3f (FVertexArray[6].X, FVertexArray[6].Y, FVertexArray[6].Z);
gl.Vertex3f (FVertexArray[2].X, FVertexArray[2].Y, FVertexArray[2].Z);
gl.Vertex3f (FVertexArray[3].X, FVertexArray[3].Y, FVertexArray[3].Z);
gl.Vertex3f (FVertexArray[7].X, FVertexArray[7].Y, FVertexArray[7].Z);
//yz, pos x
gl.Vertex3f (FVertexArray[5].X, FVertexArray[5].Y, FVertexArray[5].Z);
gl.Vertex3f (FVertexArray[1].X, FVertexArray[1].Y, FVertexArray[1].Z);
gl.Vertex3f (FVertexArray[2].X, FVertexArray[2].Y, FVertexArray[2].Z);
gl.Vertex3f (FVertexArray[6].X, FVertexArray[6].Y, FVertexArray[6].Z);
//yz, neg x
gl.Vertex3f (FVertexArray[0].X, FVertexArray[0].Y, FVertexArray[0].Z);
gl.Vertex3f (FVertexArray[4].X, FVertexArray[4].Y, FVertexArray[4].Z);
gl.Vertex3f (FVertexArray[7].X, FVertexArray[7].Y, FVertexArray[7].Z);
gl.Vertex3f (FVertexArray[3].X, FVertexArray[3].Y, FVertexArray[3].Z);
//xy, neg z
gl.Vertex3f (FVertexArray[3].X, FVertexArray[3].Y, FVertexArray[3].Z);
gl.Vertex3f (FVertexArray[2].X, FVertexArray[2].Y, FVertexArray[2].Z);
gl.Vertex3f (FVertexArray[1].X, FVertexArray[1].Y, FVertexArray[1].Z);
gl.Vertex3f (FVertexArray[0].X, FVertexArray[0].Y, FVertexArray[0].Z);
gl.End_;
end;
gl.PopMatrix;
gl.PopAttrib;
//--- added for glsl test
if initDGL then
begin
ProgHnd.EndUseProgramObject;
end;
//---
end;
- 从纹理读取颜色并通过着色器应用,工作正常。所以, 将 3D 纹理传递给着色器是可行的。
- 通过着色器进行光线投射的选项:立方体绘制有效,光线投射不起作用。
顶点着色器,用于切片和光线投射:
#version 130
varying vec3 vertCoord;
void main(void)
{
vertCoord = vec3(gl_Vertex) + vec3(0.5, 0.5, 0.5);
gl_Position = ftransform();
gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex;
}
用于切片的片段着色器,工作正常:
#version 330 core
uniform sampler3D avoxels;
in vec3 vertCoord;
out vec4 vFragColor;
void main()
{
vFragColor = texture(avoxels, vertCoord);
}
对于通过片段着色器进行光线投射,我使用了 OpenGl Development Cookbook 中的示例 glsl 代码,如 github:
#version 330 core
uniform sampler3D avoxels;
uniform vec3 acamera;
uniform vec3 astepsize;
in vec3 vertCoord;
out vec4 vFragColor;
//constants
const int MAX_SAMPLES = 300; //total samples for each ray march step
const vec3 texMin = vec3(0); //minimum texture access coordinate
const vec3 texMax = vec3(1); //maximum texture access coordinate
void main()
{
//get the 3D texture coordinates for lookup into the volume dataset
vec3 dataPos = vertCoord;
//Getting the ray marching direction:
//get the object space position by subracting 0.5 from the
//3D texture coordinates. Then subtraact it from camera position
//and normalize to get the ray marching direction
vec3 geomDir = normalize((vertCoord-vec3(0.5)) - acamera);
//multiply the raymarching direction with the step size to get the
//sub-step size we need to take at each raymarching step
vec3 dirStep = geomDir * astepsize;
//flag to indicate if the raymarch loop should terminate
bool stop = false;
float sample = 0.0;
//for all samples along the ray
for (int i = 0; i < MAX_SAMPLES; i++)
{
// advance ray by dirstep
dataPos = dataPos + dirStep;
//The two constants texMin and texMax have a value of vec3(-1,-1,-1)
//and vec3(1,1,1) respectively. To determine if the data value is
//outside the volume data, we use the sign function. The sign function
//return -1 if the value is less than 0, 0 if the value is equal to 0
//and 1 if value is greater than 0. Hence, the sign function for the
//calculation (sign(dataPos-texMin) and sign (texMax-dataPos)) will
//give us vec3(1,1,1) at the possible minimum and maximum position.
//When we do a dot product between two vec3(1,1,1) we get the answer 3.
//So to be within the dataset limits, the dot product will return a
//value less than 3. If it is greater than 3, we are already out of
//the volume dataset
stop = dot(sign(dataPos-texMin),sign(texMax-dataPos)) >= 3.0;
//if the stopping condition is true we break out of the ray marching loop
if (stop)
break;
// data fetching from the red channel of volume texture
sample = texture(avoxels, dataPos).r;
//Opacity calculation using compositing:
//here we use front to back compositing scheme whereby the current sample
//value is multiplied to the currently accumulated alpha and then this product
//is subtracted from the sample value to get the alpha from the previous steps.
//Next, this alpha is multiplied with the current sample colour and accumulated
//to the composited colour. The alpha value from the previous steps is then
//accumulated to the composited colour alpha.
float prev_alpha = sample - (sample * vFragColor.a);
vFragColor.rgb = prev_alpha * vec3(sample) + vFragColor.rgb;
vFragColor.a += prev_alpha;
//early ray termination
//if the currently composited colour alpha is already fully saturated
//we terminated the loop
if (vFragColor.a>= 1.0)
break;
}
}
在修改后的GLScene中可以看到Texture3D演示(完整的Delphi项目可以下载此处),切片有效,光线投射无效。 由于切片确实工作正常,我预计问题出在着色器代码中,或者发送到着色器的制服中。
我尝试了几种选择(主要是相机定位),但没有任何效果。
欢迎提出想法/建议。
问候,罗纳德
I use a modified version of GLScene Texture3D demo, exploring the use shaders with GLScene. I can get shaders to work, but not raycasting algorithm (single pass).
In FormCreate, a unit cube is specified:
//unit cube, for raycasting
FVertexArray[0] := Vector3fMake(-0.5, -0.5, -0.5);
FVertexArray[1] := Vector3fMake( 0.5, -0.5, -0.5);
FVertexArray[2] := Vector3fMake( 0.5, 0.5, -0.5);
FVertexArray[3] := Vector3fMake(-0.5, 0.5, -0.5);
FVertexArray[4] := Vector3fMake(-0.5, -0.5, 0.5);
FVertexArray[5] := Vector3fMake( 0.5, -0.5, 0.5);
FVertexArray[6] := Vector3fMake( 0.5, 0.5, 0.5);
FVertexArray[7] := Vector3fMake(-0.5, 0.5, 0.5);
The project uses GLScene's TGLDirectOpenGL:
procedure TForm1.GLDirectOpenGLRender(Sender: TObject; var rci: TGLRenderContextInfo);
Var
i : Integer;
step : Single;
z : Single;
mat : TMatrix;
v : TVector;
vx, vy, vz : TAffineVector;
begin
if M_3D_Texture.Handle = 0 then
begin
//Assert (GL_EXT_texture3D, 'Graphic card does not support 3D textures.');
M_3D_Texture.AllocateHandle;
gl.BindTexture (GL_TEXTURE_3D, M_3D_Texture.Handle);
gl.TexParameterf (GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP);
gl.TexParameterf (GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP);
gl.TexParameterf (GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP);
gl.TexParameteri (GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gl.TexParameteri (GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl.TexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); //GL_REPLACE);
gl.TexImage3D (GL_TEXTURE_3D, 0, GL_RGBA8, M_Output_Texture_3D.X_Size, M_Output_Texture_3D.Y_Size, M_Output_Texture_3D.Z_Size, 0, M_Output_Texture_3D.Data_Type, GL_UNSIGNED_BYTE, PChar (M_Output_Texture_3D.Data));
end;
If M_Refresh = TRUE Then
begin { then }
M_Refresh := FALSE;
Calculate_Transfer_function;
gl.TexSubImage3D (GL_TEXTURE_3D, 0, 0, 0, 0, M_Output_Texture_3D.X_Size, M_Output_Texture_3D.Y_Size, M_Output_Texture_3D.Z_Size, M_Output_Texture_3D.Data_Type, GL_UNSIGNED_BYTE, PChar (M_Output_Texture_3D.Data));
end; { then }
//--- added for glsl test
if not initDGL then
begin
if Assigned(ProgHnd) then
begin
ProgHnd.Free;
end;
ProgHnd := TGLProgramHandle.CreateAndAllocate;
ProgHnd.AddShader(TGLVertexShaderHandle,
LoadAnsiStringFromFile('.\vert.glsl'));
if not Raycasting_CB.Checked then
ProgHnd.AddShader(TGLFragmentShaderHandle,
LoadAnsiStringFromFile('.\slicing.frag.glsl'))
else
ProgHnd.AddShader(TGLFragmentShaderHandle,
LoadAnsiStringFromFile('.\\raycast.frag.glsl'));
if not ProgHnd.LinkProgram then
raise Exception.Create(ProgHnd.InfoLog);
initDGL := True;
end;
//---
gl.PushAttrib (GL_ENABLE_BIT);
gl.PushMatrix;
gl.Enable (GL_BLEND);
gl.BlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// glDisable (GL_CULL_FACE);
// glDisable (GL_LIGHTING);
//--- added for glsl test and raycasting
if initDGL then
begin
ProgHnd.UseProgramObject;
ProgHnd.UniformTextureHandle['avoxels', 0, ttTexture3D] := M_3D_Texture.Handle;
if Raycasting_CB.Checked then
begin
//camera position in cube coordinates
ProgHnd.Uniform3f['acamera'] := GLCube1.AbsoluteToLocal(GLCamera.AbsoluteAffinePosition);
with M_Output_Texture_3D do
begin
ProgHnd.Uniform3f['astepsize'] := AffineVectorMake(1/X_Size, 1/Y_Size, 1/Z_Size);
end;
end;
if not ProgHnd.ValidateProgram then
raise Exception.Create(ProgHnd.InfoLog);
end;
gl.TexGenf (GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
gl.TexGenf (GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
gl.TexGenf (GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
SetVector (v, XVector, 0.5);
gl.TexGenfv (GL_S, GL_OBJECT_PLANE, @v);
SetVector (v, YVector, 0.5);
gl.TexGenfv (GL_T, GL_OBJECT_PLANE, @v);
SetVector (v, ZVector, 0.5);
gl.TexGenfv (GL_R, GL_OBJECT_PLANE, @v);
gl.Enable (GL_TEXTURE_GEN_S);
gl.Enable (GL_TEXTURE_GEN_T);
gl.Enable (GL_TEXTURE_GEN_R);
gl.BindTexture (GL_TEXTURE_3D, M_3D_Texture.Handle);
gl.Enable (GL_TEXTURE_3D);
gl.GetFloatv (GL_MODELVIEW_MATRIX, @mat);
if not Raycasting_CB.Checked then
begin
//slicing, works ok
(*
glClipPlane(GL_CLIP_PLANE0, @clip0);
glClipPlane(GL_CLIP_PLANE1, @clip1);
glClipPlane(GL_CLIP_PLANE2, @clip2);
glClipPlane(GL_CLIP_PLANE3, @clip3);
glClipPlane(GL_CLIP_PLANE4, @clip4);
glClipPlane(GL_CLIP_PLANE5, @clip5);
glEnable(GL_CLIP_PLANE0);
glEnable(GL_CLIP_PLANE1);
glEnable(GL_CLIP_PLANE2);
glEnable(GL_CLIP_PLANE3);
glEnable(GL_CLIP_PLANE4);
glEnable(GL_CLIP_PLANE5);
*)
vx.X := mat.X.X;
vy.X := mat.X.Y;
vz.X := mat.X.Z;
vx.Y := mat.Y.X;
vy.Y := mat.Y.Y;
vz.Y := mat.Y.Z;
vx.Z := mat.Z.X;
vy.Z := mat.Z.Y;
vz.Z := mat.Z.Z;
ScaleVector (vx, DIAGONAL_LENGTH * 0.5 / VectorLength (vx));
ScaleVector (vy, DIAGONAL_LENGTH * 0.5 / VectorLength (vy));
ScaleVector (vz, DIAGONAL_LENGTH * 0.5 / VectorLength (vz));
step := DIAGONAL_LENGTH / Projection_N_TB.Position;
z := -DIAGONAL_LENGTH / 2;
gl.begin_ (GL_QUADS);
For i := 0 To Projection_N_TB.Position - 1 Do
begin
gl.Color4f (1.0, 1.0, 1.0, 1.0);
gl.Normal3f (-GLCamera.AbsoluteVectorToTarget.X, -GLCamera.AbsoluteVectorToTarget.Y, -GLCamera.AbsoluteVectorToTarget.Z);
gl.Vertex3f (vx.X + vy.X + vz.X * z, vx.Y + vy.Y + vz.Y * z, vx.Z + vy.Z + vz.Z * z);
gl.Vertex3f (-vx.X + vy.X + vz.X * z, -vx.Y + vy.Y + vz.Y * z, -vx.Z + vy.Z + vz.Z * z);
gl.Vertex3f (-vx.X - vy.X + vz.X * z, -vx.Y - vy.Y + vz.Y * z, -vx.Z - vy.Z + vz.Z * z);
gl.Vertex3f (vx.X - vy.X + vz.X * z, vx.Y - vy.Y + vz.Y * z, vx.Z - vy.Z + vz.Z * z);
z := z + step;
end;
gl.End_;
end
else
begin
//for raycasting, the idea is to draw a cube, and let de fragment shader do its work
gl.begin_ (GL_QUADS);
//xy, pos z
gl.Vertex3f (FVertexArray[4].X, FVertexArray[4].Y, FVertexArray[4].Z);
gl.Vertex3f (FVertexArray[5].X, FVertexArray[5].Y, FVertexArray[5].Z);
gl.Vertex3f (FVertexArray[6].X, FVertexArray[6].Y, FVertexArray[6].Z);
gl.Vertex3f (FVertexArray[7].X, FVertexArray[7].Y, FVertexArray[7].Z);
//xz, neg y
gl.Vertex3f (FVertexArray[4].X, FVertexArray[4].Y, FVertexArray[4].Z);
gl.Vertex3f (FVertexArray[0].X, FVertexArray[0].Y, FVertexArray[0].Z);
gl.Vertex3f (FVertexArray[1].X, FVertexArray[1].Y, FVertexArray[1].Z);
gl.Vertex3f (FVertexArray[5].X, FVertexArray[5].Y, FVertexArray[5].Z);
//xz, pos y
gl.Vertex3f (FVertexArray[6].X, FVertexArray[6].Y, FVertexArray[6].Z);
gl.Vertex3f (FVertexArray[2].X, FVertexArray[2].Y, FVertexArray[2].Z);
gl.Vertex3f (FVertexArray[3].X, FVertexArray[3].Y, FVertexArray[3].Z);
gl.Vertex3f (FVertexArray[7].X, FVertexArray[7].Y, FVertexArray[7].Z);
//yz, pos x
gl.Vertex3f (FVertexArray[5].X, FVertexArray[5].Y, FVertexArray[5].Z);
gl.Vertex3f (FVertexArray[1].X, FVertexArray[1].Y, FVertexArray[1].Z);
gl.Vertex3f (FVertexArray[2].X, FVertexArray[2].Y, FVertexArray[2].Z);
gl.Vertex3f (FVertexArray[6].X, FVertexArray[6].Y, FVertexArray[6].Z);
//yz, neg x
gl.Vertex3f (FVertexArray[0].X, FVertexArray[0].Y, FVertexArray[0].Z);
gl.Vertex3f (FVertexArray[4].X, FVertexArray[4].Y, FVertexArray[4].Z);
gl.Vertex3f (FVertexArray[7].X, FVertexArray[7].Y, FVertexArray[7].Z);
gl.Vertex3f (FVertexArray[3].X, FVertexArray[3].Y, FVertexArray[3].Z);
//xy, neg z
gl.Vertex3f (FVertexArray[3].X, FVertexArray[3].Y, FVertexArray[3].Z);
gl.Vertex3f (FVertexArray[2].X, FVertexArray[2].Y, FVertexArray[2].Z);
gl.Vertex3f (FVertexArray[1].X, FVertexArray[1].Y, FVertexArray[1].Z);
gl.Vertex3f (FVertexArray[0].X, FVertexArray[0].Y, FVertexArray[0].Z);
gl.End_;
end;
gl.PopMatrix;
gl.PopAttrib;
//--- added for glsl test
if initDGL then
begin
ProgHnd.EndUseProgramObject;
end;
//---
end;
- color is read from texture and applied via shaders, works ok. So,
passing a 3D texture to shaders works. - option for raycasting via shaders : cube drawing works, raycasting does not does not work.
Vertex shader, used for both slicing and raycasting:
#version 130
varying vec3 vertCoord;
void main(void)
{
vertCoord = vec3(gl_Vertex) + vec3(0.5, 0.5, 0.5);
gl_Position = ftransform();
gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex;
}
Fragment shader for slicing, works ok:
#version 330 core
uniform sampler3D avoxels;
in vec3 vertCoord;
out vec4 vFragColor;
void main()
{
vFragColor = texture(avoxels, vertCoord);
}
For the raycasting via the fragment shader, I used the example glsl code from the OpenGl Development Cookbook, as found on github:
#version 330 core
uniform sampler3D avoxels;
uniform vec3 acamera;
uniform vec3 astepsize;
in vec3 vertCoord;
out vec4 vFragColor;
//constants
const int MAX_SAMPLES = 300; //total samples for each ray march step
const vec3 texMin = vec3(0); //minimum texture access coordinate
const vec3 texMax = vec3(1); //maximum texture access coordinate
void main()
{
//get the 3D texture coordinates for lookup into the volume dataset
vec3 dataPos = vertCoord;
//Getting the ray marching direction:
//get the object space position by subracting 0.5 from the
//3D texture coordinates. Then subtraact it from camera position
//and normalize to get the ray marching direction
vec3 geomDir = normalize((vertCoord-vec3(0.5)) - acamera);
//multiply the raymarching direction with the step size to get the
//sub-step size we need to take at each raymarching step
vec3 dirStep = geomDir * astepsize;
//flag to indicate if the raymarch loop should terminate
bool stop = false;
float sample = 0.0;
//for all samples along the ray
for (int i = 0; i < MAX_SAMPLES; i++)
{
// advance ray by dirstep
dataPos = dataPos + dirStep;
//The two constants texMin and texMax have a value of vec3(-1,-1,-1)
//and vec3(1,1,1) respectively. To determine if the data value is
//outside the volume data, we use the sign function. The sign function
//return -1 if the value is less than 0, 0 if the value is equal to 0
//and 1 if value is greater than 0. Hence, the sign function for the
//calculation (sign(dataPos-texMin) and sign (texMax-dataPos)) will
//give us vec3(1,1,1) at the possible minimum and maximum position.
//When we do a dot product between two vec3(1,1,1) we get the answer 3.
//So to be within the dataset limits, the dot product will return a
//value less than 3. If it is greater than 3, we are already out of
//the volume dataset
stop = dot(sign(dataPos-texMin),sign(texMax-dataPos)) >= 3.0;
//if the stopping condition is true we break out of the ray marching loop
if (stop)
break;
// data fetching from the red channel of volume texture
sample = texture(avoxels, dataPos).r;
//Opacity calculation using compositing:
//here we use front to back compositing scheme whereby the current sample
//value is multiplied to the currently accumulated alpha and then this product
//is subtracted from the sample value to get the alpha from the previous steps.
//Next, this alpha is multiplied with the current sample colour and accumulated
//to the composited colour. The alpha value from the previous steps is then
//accumulated to the composited colour alpha.
float prev_alpha = sample - (sample * vFragColor.a);
vFragColor.rgb = prev_alpha * vec3(sample) + vFragColor.rgb;
vFragColor.a += prev_alpha;
//early ray termination
//if the currently composited colour alpha is already fully saturated
//we terminated the loop
if (vFragColor.a>= 1.0)
break;
}
}
As can be seen in the modified GLScene Texture3D demo (complete Delphi project can be downloaded here), slicing works, raycasting does not.
As slicing does work OK, I expect the problem to be in shader code, or in uniforms sent to shader.
I tried several options (mainly camera positioning), but nothing works.
Ideas/suggestions are welcome.
Regards, Ronald
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论