您的位置:首页 > 编程语言 > C#

CSharpGL(25)一个用raycast实现体渲染VolumeRender的例子

2016-05-31 01:42 866 查看
[b]CSharpGL(25)一个用raycast实现体渲染VolumeRender的例子 [/b]

本文涉及的VolumeRendering相关的C#代码是从(https://github.com/toolchainX/Volume_Rendering_Using_GLSL)的C++代码转换来的。

效果图

protected override void DoInitialize()
{
InitBackfaceRenderer();

InitRaycastRenderer();

initTFF1DTex(@"10RaycastVolumeRender\tff.dat");
int[] viewport = OpenGL.GetViewport();
initFace2DTex(viewport[2], viewport[3]);
initVol3DTex(@"10RaycastVolumeRender\head256.raw", 256, 256, 225);
initFrameBuffer(viewport[2], viewport[3]);

//this.depthTest = new DepthTestSwitch();

RaycastingSetupUniforms();
}

private void RaycastingSetupUniforms()
{
// setting uniforms such as
// ScreenSize
// StepSize
// TransferFunc
// ExitPoints i.e. the backface, the backface hold the ExitPoints of ray casting
// VolumeTex the texture that hold the volume data i.e. head256.raw
int[] viewport = OpenGL.GetViewport();
this.raycastRenderer.SetUniform("ScreenSize", new vec2(viewport[2], viewport[3]));
this.raycastRenderer.SetUniform("StepSize", g_stepSize);
this.raycastRenderer.SetUniform("TransferFunc", new samplerValue(BindTextureTarget.Texture1D, transferFunc1DTexObj[0], OpenGL.GL_TEXTURE0));
this.raycastRenderer.SetUniform("ExitPoints", new samplerValue(BindTextureTarget.Texture2D, backface2DTexObj[0], OpenGL.GL_TEXTURE1));
this.raycastRenderer.SetUniform("VolumeTex", new samplerValue(BindTextureTarget.Texture3D, vol3DTexObj[0], OpenGL.GL_TEXTURE2));
var clearColor = new float[4];
OpenGL.GetFloat(GetTarget.ColorClearValue, clearColor);
this.raycastRenderer.SetUniform("backgroundColor", clearColor.ToVec4());
}

private void initFrameBuffer(int texWidth, int texHeight)
{
// create a depth buffer for our framebuffer
var depthBuffer = new uint[1];
OpenGL.GetDelegateFor<OpenGL.glGenRenderbuffersEXT>()(1, depthBuffer);
OpenGL.GetDelegateFor<OpenGL.glBindRenderbufferEXT>()(OpenGL.GL_RENDERBUFFER, depthBuffer[0]);
OpenGL.GetDelegateFor<OpenGL.glRenderbufferStorageEXT>()(OpenGL.GL_RENDERBUFFER, OpenGL.GL_DEPTH_COMPONENT, texWidth, texHeight);

// attach the texture and the depth buffer to the framebuffer
OpenGL.GetDelegateFor<OpenGL.glGenFramebuffersEXT>()(1, frameBuffer);
OpenGL.GetDelegateFor<OpenGL.glBindFramebufferEXT>()(OpenGL.GL_FRAMEBUFFER_EXT, frameBuffer[0]);
OpenGL.GetDelegateFor<OpenGL.glFramebufferTexture2DEXT>()(OpenGL.GL_FRAMEBUFFER_EXT, OpenGL.GL_COLOR_ATTACHMENT0_EXT, OpenGL.GL_TEXTURE_2D, backface2DTexObj[0], 0);
OpenGL.GetDelegateFor<OpenGL.glFramebufferRenderbufferEXT>()(OpenGL.GL_FRAMEBUFFER_EXT, OpenGL.GL_DEPTH_ATTACHMENT_EXT, OpenGL.GL_RENDERBUFFER, depthBuffer[0]);
checkFramebufferStatus();
//OpenGL.Enable(GL_DEPTH_TEST);
}

private void checkFramebufferStatus()
{
uint complete = OpenGL.GetDelegateFor<OpenGL.glCheckFramebufferStatusEXT>()(OpenGL.GL_FRAMEBUFFER_EXT);
if (complete != OpenGL.GL_FRAMEBUFFER_COMPLETE_EXT)
{
throw new Exception("framebuffer is not complete");
}
}

private void initVol3DTex(string filename, int width, int height, int depth)
{
var data = new UnmanagedArray<byte>(width * height * depth);
unsafe
{
int index = 0;
int readCount = 0;
byte* array = (byte*)data.Header.ToPointer();
using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
using (var br = new BinaryReader(fs))
{
int unReadCount = (int)fs.Length;
const int cacheSize = 1024 * 1024;
do
{
int min = Math.Min(cacheSize, unReadCount);
var cache = new byte[min];
readCount = br.Read(cache, 0, min);
if (readCount != min)
{ throw new Exception(); }

for (int i = 0; i < readCount; i++)
{
array[index++] = cache[i];
}
unReadCount -= readCount;
} while (readCount > 0);
}
}

OpenGL.GenTextures(1, vol3DTexObj);
// bind 3D texture target
OpenGL.BindTexture(OpenGL.GL_TEXTURE_3D, vol3DTexObj[0]);
OpenGL.TexParameteri(OpenGL.GL_TEXTURE_3D, OpenGL.GL_TEXTURE_MAG_FILTER, (int)OpenGL.GL_LINEAR);
OpenGL.TexParameteri(OpenGL.GL_TEXTURE_3D, OpenGL.GL_TEXTURE_MIN_FILTER, (int)OpenGL.GL_LINEAR);
OpenGL.TexParameteri(OpenGL.GL_TEXTURE_3D, OpenGL.GL_TEXTURE_WRAP_S, (int)OpenGL.GL_REPEAT);
OpenGL.TexParameteri(OpenGL.GL_TEXTURE_3D, OpenGL.GL_TEXTURE_WRAP_T, (int)OpenGL.GL_REPEAT);
OpenGL.TexParameteri(OpenGL.GL_TEXTURE_3D, OpenGL.GL_TEXTURE_WRAP_R, (int)OpenGL.GL_REPEAT);
// pixel transfer happens here from client to OpenGL server
OpenGL.PixelStorei(OpenGL.GL_UNPACK_ALIGNMENT, 1);
OpenGL.TexImage3D(OpenGL.GL_TEXTURE_3D, 0, (int)OpenGL.GL_INTENSITY,
width, height, depth, 0,
OpenGL.GL_LUMINANCE, OpenGL.GL_UNSIGNED_BYTE, data.Header);
data.Dispose();
}

private void initFace2DTex(int width, int height)
{
OpenGL.GenTextures(1, backface2DTexObj);
OpenGL.BindTexture(OpenGL.GL_TEXTURE_2D, backface2DTexObj[0]);
OpenGL.TexParameteri(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_WRAP_S, (int)OpenGL.GL_REPEAT);
OpenGL.TexParameteri(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_WRAP_T, (int)OpenGL.GL_REPEAT);
OpenGL.TexParameteri(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MIN_FILTER, (int)OpenGL.GL_NEAREST);
OpenGL.TexParameteri(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MAG_FILTER, (int)OpenGL.GL_NEAREST);
OpenGL.TexImage2D(OpenGL.GL_TEXTURE_2D, 0, OpenGL.GL_RGBA16F, width, height, 0, OpenGL.GL_RGBA, OpenGL.GL_FLOAT, IntPtr.Zero);
}

private void initTFF1DTex(string filename)
{
// read in the user defined data of transfer function
byte[] tff;
using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
using (var br = new BinaryReader(fs))
{
tff = br.ReadBytes((int)fs.Length);
}
OpenGL.GenTextures(1, transferFunc1DTexObj);
OpenGL.BindTexture(OpenGL.GL_TEXTURE_1D, transferFunc1DTexObj[0]);
OpenGL.TexParameteri(OpenGL.GL_TEXTURE_1D, OpenGL.GL_TEXTURE_WRAP_S, (int)OpenGL.GL_REPEAT);
OpenGL.TexParameteri(OpenGL.GL_TEXTURE_1D, OpenGL.GL_TEXTURE_MIN_FILTER, (int)OpenGL.GL_NEAREST);
OpenGL.TexParameteri(OpenGL.GL_TEXTURE_1D, OpenGL.GL_TEXTURE_MAG_FILTER, (int)OpenGL.GL_NEAREST);
OpenGL.PixelStorei(OpenGL.GL_UNPACK_ALIGNMENT, 1);
OpenGL.TexImage1D(OpenGL.GL_TEXTURE_1D, 0, OpenGL.GL_RGBA8, 256, 0, OpenGL.GL_RGBA, OpenGL.GL_UNSIGNED_BYTE, tff);
}

private void InitRaycastRenderer()
{
var shaderCodes = new ShaderCode[2];
shaderCodes[0] = new ShaderCode(File.ReadAllText(@"10RaycastVolumeRender\raycasting.vert"), ShaderType.VertexShader);
shaderCodes[1] = new ShaderCode(File.ReadAllText(@"10RaycastVolumeRender\raycasting.frag"), ShaderType.FragmentShader);
var map = new PropertyNameMap();
map.Add("position", "position");
map.Add("color", "color");
this.raycastRenderer = new Renderer(model, shaderCodes, map);
this.raycastRenderer.Initialize();
this.raycastRenderer.SwitchList.Add(new CullFaceSwitch(CullFaceMode.Back, true));
}

private void InitBackfaceRenderer()
{
var shaderCodes = new ShaderCode[2];
shaderCodes[0] = new ShaderCode(File.ReadAllText(@"10RaycastVolumeRender\backface.vert"), ShaderType.VertexShader);
shaderCodes[1] = new ShaderCode(File.ReadAllText(@"10RaycastVolumeRender\backface.frag"), ShaderType.FragmentShader);
var map = new PropertyNameMap();
map.Add("position", "position");
map.Add("color", "color");
this.backfaceRenderer = new Renderer(model, shaderCodes, map);
this.backfaceRenderer.Initialize();
this.backfaceRenderer.SwitchList.Add(new CullFaceSwitch(CullFaceMode.Front, true));
}


Initialize

总结

当然,也可以先渲染出起始点,然后再找到终点的时候计算各个像素点的颜色值。

raycast做volume rendering的这个例子中,最耗空间的是3D纹理。但是这是无法避免的。其他空间和时间耗费都是极少的。

欢迎对OpenGL有兴趣的同学关注(https://github.com/bitzhuwei/CSharpGL
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: