使用光线投射的体积渲染-在体积中流动



我正在开发我的体积渲染应用程序(C#+OpenTK)。该卷正在使用光线投射进行渲染,我在这个网站上找到了很多灵感:http://graphicsrunner.blogspot.sk/2009/01/volume-rendering-101.html,尽管我的应用程序使用OpenGL,但使用3D纹理和其他东西的主要思想是一样的。应用程序运行良好,但在我"流入体积"(意味着在边界框内)之后,一切都消失了,我想防止这种情况发生。那么,有什么简单的方法可以做到这一点吗?-->我将能够在音量中流动或在音量中移动。

这是碎片着色器的代码:

#version 330
in vec3 EntryPoint;
in vec4 ExitPointCoord;
uniform sampler2D ExitPoints;
uniform sampler3D VolumeTex;
uniform sampler1D TransferFunc;  
uniform float     StepSize;
uniform float     AlphaReduce;
uniform vec2      ScreenSize;
layout (location = 0) out vec4 FragColor;
void main()
{
//gl_FragCoord --> http://www.txutxi.com/?p=182
vec3 exitPoint = texture(ExitPoints, gl_FragCoord.st/ScreenSize).xyz;
//background need no raycasting
if (EntryPoint == exitPoint)
    discard;
vec3 rayDirection = normalize(exitPoint - EntryPoint);
vec4 currentPosition = vec4(EntryPoint, 0.0f);
vec4 colorSum = vec4(.0f,.0f,.0f,.0f);
vec4 color = vec4(0.0f,0.0f,0.0f,0.0f);
vec4 value = vec4(0.0f);
vec3 Step = rayDirection * StepSize;
float stepLength= length(Step);
float LengthSum = 0.0f;
float Length = length(exitPoint - EntryPoint);
for(int i=0; i < 16000; i++)
{
    currentPosition.w = 0.0f;
    value = texture(VolumeTex, currentPosition.xyz);
    color = texture(TransferFunc, value.a);
    //reduce the alpha to have a more transparent result
    color.a *= AlphaReduce;
    //Front to back blending
    color.rgb *= color.a;
    colorSum = (1.0f - colorSum.a) * color + colorSum;
    //accumulate length
    LengthSum += stepLength;
    //break from the loop when alpha gets high enough
    if(colorSum.a >= .95f)
        break;
    //advance the current position
    currentPosition.xyz += Step;
    //break if the ray is outside of the bounding box
    if(LengthSum >= Length)
        break;
}
FragColor = colorSum;
}

以下代码基于https://github.com/toolchainX/Volume_Rendering_Using_GLSL

Display()函数:

    public void Display()
    {
        // the color of the vertex in the back face is also the location
        // of the vertex
        // save the back face to the user defined framebuffer bound
        // with a 2D texture named `g_bfTexObj`
        // draw the front face of the box
        // in the rendering process, i.e. the ray marching process
        // loading the volume `g_volTexObj` as well as the `g_bfTexObj`
        // after vertex shader processing we got the color as well as the location of
        // the vertex (in the object coordinates, before transformation).
        // and the vertex assemblied into primitives before entering
        // fragment shader processing stage.
        // in fragment shader processing stage. we got `g_bfTexObj`
        // (correspond to 'VolumeTex' in glsl)and `g_volTexObj`(correspond to 'ExitPoints')
        // as well as the location of primitives.
        // draw the back face of the box
        GL.Enable(EnableCap.DepthTest);
        //"vykreslim" front || back face objemu do framebuffru --> teda do 2D textury s ID bfTexID 
        //(pomocou backface.frag &.vert)
        GL.BindFramebuffer(FramebufferTarget.Framebuffer, frameBufferID);
        GL.Viewport(0, 0, width, height);
        LinkShader(spMain.GetProgramHandle(), bfVertShader.GetShaderHandle(), bfFragShader.GetShaderHandle());
        spMain.UseProgram();
        //cull front face
        Render(CullFaceMode.Front);
        spMain.UseProgram(0);
        //klasicky framebuffer --> "obrazovka"
        GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
        GL.Viewport(0, 0, width, height);
        LinkShader(spMain.GetProgramHandle(), rcVertShader.GetShaderHandle(), rcFragShader.GetShaderHandle());
        spMain.UseProgram();
        SetUniforms();
        Render(CullFaceMode.Back);
        spMain.UseProgram(0);
        GL.Disable(EnableCap.DepthTest);
    }
    private void DrawBox(CullFaceMode mode)
    {
        // --> Face culling allows non-visible triangles of closed surfaces to be culled before expensive Rasterization and Fragment Shader operations.
        GL.Enable(EnableCap.CullFace);
        GL.CullFace(mode);
        GL.BindVertexArray(VAO);
        GL.DrawElements(PrimitiveType.Triangles, 36, DrawElementsType.UnsignedInt, 0);
        GL.BindVertexArray(0);
        GL.Disable(EnableCap.CullFace);
        spMain.UseProgram(0);//zapnuty bol v Render() ktora DrawBox zavolala
    }
    private void Render(CullFaceMode mode)
    {
        GL.ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
        spMain.UseProgram();
        spMain.SetUniform("modelViewMatrix", Current);
        spMain.SetUniform("projectionMatrix", projectionMatrix);
        DrawBox(mode);
    }

问题是(我认为)当我朝着体积移动时(我不移动相机,只是缩放体积),如果比例因子>2.7左右,我就在体积中,这意味着"在渲染最终图片的平面之后",所以a什么都看不见。我能想到的解决方案(也许)是这样的:如果我达到比例因子=2.7左右:

1.)->不要缩放的音量

2.)->不知怎么地告诉碎片着色器将EntryPoint移向一定长度的光线方向(可能基于比例因子)。

现在,我尝试了这种"方法",它似乎可以工作:

vec3 entryPoint = EntryPoint + some_value * rayDirection;

some_value必须固定在[0,1]interval(或[0,1]?)之间,但也许没关系,谢谢:

if (EntryPoint == exitPoint)
discard;

所以现在,也许(如果我的解决方案不是那么糟糕的话),我可以改变我的答案:如何计算some_value(基于我发送给片段着色器的比例因子)?

if(scale_factor < 2.7something)
    work like before;
else
{
    compute some_value; //(I need help with this part)
    change entry point;
    work like before;
}

谢谢。

我解决了我的问题。它不会让"被音量包围"产生幻觉,但现在,我可以在音量中流动,什么都没有消失。这是我添加到片段着色器的解决方案的代码:

vec3 entryPoint = vec3(0.0f);
if(scaleCoeff >= 2.7f)
{
    float tmp = min((scaleCoeff - 2.7f) * 0.1f, 1.0f);
    entryPoint = EntryPoint + tmp * (exitPoint - EntryPoint);
}
else
{
    entryPoint = EntryPoint;
}
//

但如果你知道或能想出更好的解决方案来产生"被音量包围"的效果,如果你告诉我,我会很高兴。

谢谢。

如果理解正确,我认为您应该使用平面剪裁来浏览该卷。(如果你附上这个解决方案,我可以根据你的代码给你一个简单的例子。将整个C++项目翻译成C#太耗时了。)

最新更新