Объемный рендеринг с использованием raycasting — прохождение через объем

Я работаю над своим приложением для объемного рендеринга (C# + OpenTK). Объем визуализируется с использованием raycasting, я нашел много вдохновения на этом сайте: 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.

Функция отображения():

    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 с чем-то, я нахожусь в объеме, это означает «после плоскость, на которой визуализируется окончательное изображение», поэтому ничего не видно. Решение (возможно), которое я могу придумать, примерно такое: если я достигну масштабного коэффициента = 2,7 что-то:

1.) -> не масштабировать громкость

2.) -> как-то сказал фрагментному шейдеру переместить EntryPoint в направлении RayDirection на некоторую длину (вероятно, исходя из коэффициента масштабирования).

Теперь я попробовал этот «метод», и кажется, что он может работать:

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;
}

(Я не носитель английского языка, поэтому, если в тексте есть какие-то большие ошибки и вы что-то не понимаете, просто дайте мне знать, и я постараюсь исправить эти ошибки)

Спасибо.


person imo    schedule 05.04.2016    source источник
comment
Чтобы эффективно помочь вам, нам нужно увидеть код.   -  person Joel Brewer    schedule 05.04.2016


Ответы (2)


Я решил свою проблему. Это не создает иллюзию «быть окруженным объемом», но теперь я могу течь сквозь объем, и ничего не исчезает. Это код моего решения, добавленного во фрагментный шейдер:

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;
}
//

Но если вы знаете или можете подумать о лучшем решении, создающем эффект «окружения объемом», я буду рад, если вы сообщите мне об этом.

Спасибо.

person imo    schedule 07.04.2016

Если вы правильно поняли, я думаю, что вы должны использовать Plane Clipping, чтобы пройти через объем. (Я мог бы привести вам простой пример на основе вашего кода, если вы приложите это решение. Перевод всего проекта C++ на C# занимает слишком много времени.)

person J. Sue    schedule 13.03.2019