体绘制(Volume Rendering)概述之4:光线投射算法(Ray Casting)实现流程和代码(基于CPU的实现)

摘抄“GPU Programming And Cg Language Primer 1rd Edition” 中文 名“GPU编程与CG语言之阳春白雪下里巴人”    

 

 

算法流程



47 展示了使用光线投射算法进行体绘制的实现流程。

首先要渲染出正向面深度图和背向面深度图,这是为了计算射线穿越的最大距离,做为循环采样控制的结束依据;然后在顶点着色程序中计算顶点位置和射线方向,射线方向由视线方向和点的世界坐标决定,其实射线方向也可以放在片段着色程序中进行计算。然后到了最关键的地方,就是循环纹理采样、合成。

每一次循环都要计算新的采样纹理坐标和采样距离,然后进行颜色合成和透明度累加,如果采样距离超过了最大穿越距离,或者透明度累加到 1 ,则循环结束。将合成得到的颜色值输出即可。

48 给出了使用光线投射算法进行体绘制的效果图:


15.4 光线投射算法实现

本节给出光线投射算法的着色程序实现代码。依然是分为三个部分:结构体、顶点着色程序和片段着色程序。

代码 22 光线投射算法结构体

 

 

struct VertexIn

{

  float4 position : POSITION;    

  float4 texCoord:  TEXCOORD;

};

 

struct VertexScreen

{

  float4 position   : POSITION; 

  float4 worldPos   : TEXCOORD0;

  float4 projPos    : TEXCOORD1;

  float4 texCoord   : TEXCOORD2;

};

代码 23 光线投射算法顶点着色程序

 

 

VertexScreen main_v(VertexIn posIn,

                        uniform float4x4 world,

                  uniform float4x4 worldViewProj,

                  uniform float4x4 texViewProj)

{

    VertexScreen posOut;

   

    posOut.position = mul(worldViewProj, posIn.position);

    posOut.worldPos = mul(world,posIn.position);

    posOut.projPos = mul(texViewProj, posOut.worldPos); 

    posOut.texCoord = posIn.texCoord;

 

    return posOut;

}

 

代码 24 光线投射算法片段着色程序

 

void main_f(VertexScreen posIn,

           uniform float3 eyePosition,

           uniform sampler3D volumeTex: register(s0),

           uniform sampler2D frontDepthTex: register(s1) ,

           uniform sampler2D backDepthTex: register(s2) ,

        out float4 result        : COLOR)

{

       // 根据视点和当前顶点世界坐标计算方向

       float3 dir = posIn.worldPos.xyz-eyePosition;

       dir = normalize(dir);

       float3 deltaDir = float3(0.0, 0.0, 0.0);

      

       // 获取当前顶点的三维纹理坐标

       float3 tex = posIn.texCoord.xyz;

       float2 uvDelta;

       uvDelta.x = 0.0;//ddx( tex ).x;

       uvDelta.y = 0.0;//ddy( tex ).y;

      

       // 取出深度间隔值 , 并设置采样间隔

       float2 uv= posIn.projPos.xy/posIn.projPos.w;

       float frontDis = tex2D(frontDepthTex,uv).x;

       float backDis = tex2D(backDepthTex,uv).x;

       float len = backDis-frontDis;

      

       // 初始化颜色值、采样值、透明度

  float3 norm_dir = normalize(dir);

  float stepsize = 0.01;

  float delta = stepsize;

  float3 delta_dir = norm_dir * delta;

  float delta_dir_len = length(delta_dir);

  float3 vec = posIn.texCoord.xyz;

  float4 col_acc = float4(0,0,0,0);

  float alpha_acc = 0;

  float length_acc = 0;

  float4 color_sample;

  float alpha_sample;

 

  for(int i = 0; i < 800; i++){

       color_sample = tex3D(volumeTex,vec);

      alpha_sample = color_sample.a * stepsize;

      col_acc   += (1.0 - alpha_acc) * color_sample * alpha_sample * 3;

      alpha_acc += alpha_sample;

      vec += delta_dir;

      length_acc += delta_dir_len;

      if(length_acc >= len || alpha_acc > 1.0) break; // 采样循环控制条件

 }

       result.xyz = col_acc.xyz*2.0+float3(0.2,0.2,0.2);

       result.w = col_acc.w;  

}

15.5 本章小结

本书的第1415 章阐述了体绘制中光线投射算法的基本原理和实现流程。实际上,在此基础上可以对光线投射算法加以扩展,例如将光线投射算法和阴影绘制算法相结合,可以渲染出真实感更强的图像。

此外,有些体数据是中间是空的,在射线方向上进行采样时需要跳过空区域,这其中也需要额外的算法处理,在英文中称为“Object-Order Empty Space Skipping ”。

目前我所发现关于体绘制以及光线投射算法最好的教材是Markus Hadwiger 等人所写的“Advanced Illumination Techniques for GPU-Based Volume Raycasting ”。此书发表在SIGGRAPH ASIA2008 上,是目前所能找到最新也是非常权威的教材,共166 页。英文阅读能力比较好的同学可以尝试着看一下。

本章已经是此书的最后一章,最后希望中国的计算机科学可以真正上升到科学研究的层次,而不是一直在混沌中热衷做泥瓦匠的工作。

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 以下是一个基于DICOM序列图像的VTK绘制的C代码示例: ``` #include <vtkSmartPointer.h> #include <vtkDICOMImageReader.h> #include <vtkVolume.h> #include <vtkVolumeProperty.h> #include <vtkColorTransferFunction.h> #include <vtkPiecewiseFunction.h> #include <vtkFixedPointVolumeRayCastMapper.h> #include <vtkVolumeRayCastCompositeFunction.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> int main() { // 创建DICOM图像读取器 vtkSmartPointer<vtkDICOMImageReader> reader = vtkSmartPointer<vtkDICOMImageReader>::New(); reader->SetDirectoryName("DICOM序列图像文件夹路径"); reader->Update(); // 创建颜色传递函数 vtkSmartPointer<vtkColorTransferFunction> colorTransferFunction = vtkSmartPointer<vtkColorTransferFunction>::New(); colorTransferFunction->AddRGBPoint(0, 0.0, 0.0, 0.0); colorTransferFunction->AddRGBPoint(255, 1.0, 1.0, 1.0); // 创建灰度传递函数 vtkSmartPointer<vtkPiecewiseFunction> opacityTransferFunction = vtkSmartPointer<vtkPiecewiseFunction>::New(); opacityTransferFunction->AddPoint(0, 0.0); opacityTransferFunction->AddPoint(255, 1.0); // 创建组合传递函数 vtkSmartPointer<vtkVolumeProperty> volumeProperty = vtkSmartPointer<vtkVolumeProperty>::New(); volumeProperty->SetColor(colorTransferFunction); volumeProperty->SetScalarOpacity(opacityTransferFunction); volumeProperty->ShadeOff(); volumeProperty->SetInterpolationTypeToLinear(); // 创建数据映射器 vtkSmartPointer<vtkFixedPointVolumeRayCastMapper> volumeMapper = vtkSmartPointer<vtkFixedPointVolumeRayCastMapper>::New(); volumeMapper->SetInputConnection(reader->GetOutputPort()); // 创建积 vtkSmartPointer<vtkVolume> volume = vtkSmartPointer<vtkVolume>::New(); volume->SetMapper(volumeMapper); volume->SetProperty(volumeProperty); // 创建渲染器并添加积 vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New(); renderer->AddVolume(volume); // 创建渲染窗口并设置渲染器 vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->AddRenderer(renderer); // 创建交互式操作器并设置渲染窗口 vtkSmartPointer<vtkRenderWindowInteractor> interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); interactor->SetRenderWindow(renderWindow); // 开始渲染 interactor->Initialize(); interactor->Start(); return 0; } ``` 该代码使用VTK库创建了一个DICOM图像的绘制的demo。首先,通过vtkDICOMImageReader读取DICOM序列图像的文件夹路径。然后,创建颜色传递函数和灰度传递函数用于对数据进行颜色和透明度的控制。接下来,使用vtkFixedPointVolumeRayCastMapper将DICOM图像数据映射为数据,并设置相应的传递函数。再创建一个vtkVolume对象来包含数据和属性。然后,创建一个vtkRenderer并将积添加到其中。接下来,创建一个vtkRenderWindow并将渲染器添加到其中。最后,创建一个vtkRenderWindowInteractor,并将渲染窗口设置给它,并开始渲染。 ### 回答2: 以下是一个基于DICOM序列图像的C代码示例,用于vtk绘制和三维重建的演示。 ```c #include <vtkSmartPointer.h> #include <vtkDICOMImageReader.h> #include <vtkFixedPointVolumeRayCastMapper.h> #include <vtkColorTransferFunction.h> #include <vtkPiecewiseFunction.h> #include <vtkVolumeProperty.h> #include <vtkVolume.h> #include <vtkRenderWindow.h> #include <vtkRenderer.h> #include <vtkRenderWindowInteractor.h> int main(int argc, char* argv[]) { if (argc < 2) { std::cerr << "Usage: " << argv[0] << " <DICOM Directory>" << std::endl; return EXIT_FAILURE; } // 创建DICOM图像阅读器 vtkSmartPointer<vtkDICOMImageReader> reader = vtkSmartPointer<vtkDICOMImageReader>::New(); reader->SetDirectoryName(argv[1]); reader->Update(); // 创建渲染Mapper vtkSmartPointer<vtkFixedPointVolumeRayCastMapper> mapper = vtkSmartPointer<vtkFixedPointVolumeRayCastMapper>::New(); mapper->SetInputConnection(reader->GetOutputPort()); // 创建颜色转换函数 vtkSmartPointer<vtkColorTransferFunction> colorFun = vtkSmartPointer<vtkColorTransferFunction>::New(); colorFun->AddRGBPoint(0, 0.0, 0.0, 0.0); colorFun->AddRGBPoint(255, 1.0, 1.0, 1.0); // 创建灰度转换函数 vtkSmartPointer<vtkPiecewiseFunction> opacityFun = vtkSmartPointer<vtkPiecewiseFunction>::New(); opacityFun->AddPoint(0, 0.0); opacityFun->AddPoint(255, 1.0); // 创建属性 vtkSmartPointer<vtkVolumeProperty> volumeProperty = vtkSmartPointer<vtkVolumeProperty>::New(); volumeProperty->SetColor(colorFun); volumeProperty->SetScalarOpacity(opacityFun); volumeProperty->ShadeOn(); volumeProperty->SetInterpolationTypeToLinear(); // 创建 vtkSmartPointer<vtkVolume> volume = vtkSmartPointer<vtkVolume>::New(); volume->SetMapper(mapper); volume->SetProperty(volumeProperty); // 创建渲染器和窗口 vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New(); vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->AddRenderer(renderer); vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); renderWindowInteractor->SetRenderWindow(renderWindow); // 将添加到渲染器中 renderer->AddViewProp(volume); // 渲染和开始交互 renderWindow->Render(); renderWindowInteractor->Start(); return EXIT_SUCCESS; } ``` 这个示例代码使用vtkDICOMImageReader加载DICOM序列图像,然后通过vtkFixedPointVolumeRayCastMapper创建渲染映射器。然后,使用vtkColorTransferFunction设置颜色转换函数和vtkPiecewiseFunction设置灰度转换函数来定义的颜色和透明度。创建一个vtkVolumeProperty,并将之前创建的转换函数和其他属性设置给它,然后创建一个vtkVolume并将之前创建的映射器和属性设置给它。最后,将该添加到vtkRenderer中,并通过创建vtkRenderWindow和vtkRenderWindowInteractor来显示渲染结果并启动交互。 ### 回答3: 以下是一个基于DICOM序列图像的C++代码示例,用于绘制vtk绘制(volume rendering)或三维重建的demo: ```c++ #include <vtkSmartPointer.h> #include <vtkDICOMImageReader.h> #include <vtkVolume.h> #include <vtkVolumeMapper.h> #include <vtkVolumeProperty.h> #include <vtkFixedPointVolumeRayCastMapper.h> #include <vtkPiecewiseFunction.h> #include <vtkColorTransferFunction.h> #include <vtkRenderWindow.h> #include <vtkRenderer.h> #include <vtkRenderWindowInteractor.h> int main(int argc, char *argv[]) { // 创建DICOM图像阅读器 vtkSmartPointer<vtkDICOMImageReader> reader = vtkSmartPointer<vtkDICOMImageReader>::New(); reader->SetDirectoryName("DICOM文件夹路径"); // 修改为DICOM数据所在文件夹路径 reader->Update(); // 创建绘制的映射器和绘制属性 vtkSmartPointer<vtkFixedPointVolumeRayCastMapper> mapper = vtkSmartPointer<vtkFixedPointVolumeRayCastMapper>::New(); mapper->SetInputConnection(reader->GetOutputPort()); vtkSmartPointer<vtkVolumeProperty> volumeProperty = vtkSmartPointer<vtkVolumeProperty>::New(); volumeProperty->ShadeOff(); // 关闭阴影效果 // 创建透明度函数,并设置透明度映射 vtkSmartPointer<vtkPiecewiseFunction> opacityFunction = vtkSmartPointer<vtkPiecewiseFunction>::New(); opacityFunction->AddPoint(0, 0.0); opacityFunction->AddPoint(700, 0.1); opacityFunction->AddPoint(1400, 0.2); opacityFunction->AddPoint(2000, 0.4); volumeProperty->SetScalarOpacity(opacityFunction); // 创建颜色函数,并设置颜色映射 vtkSmartPointer<vtkColorTransferFunction> colorFunction = vtkSmartPointer<vtkColorTransferFunction>::New(); colorFunction->AddRGBPoint(0, 0.0, 0.0, 0.0); colorFunction->AddRGBPoint(700, 1.0, 0.5, 0.3); colorFunction->AddRGBPoint(1400, 1.0, 0.7, 0.5); colorFunction->AddRGBPoint(2000, 1.0, 1.0, 0.9); volumeProperty->SetColor(colorFunction); vtkSmartPointer<vtkVolume> volume = vtkSmartPointer<vtkVolume>::New(); volume->SetMapper(mapper); volume->SetProperty(volumeProperty); // 创建渲染器和渲染窗口 vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New(); vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->AddRenderer(renderer); // 创建交互器 vtkSmartPointer<vtkRenderWindowInteractor> interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); interactor->SetRenderWindow(renderWindow); // 将绘制添加到渲染器中 renderer->AddVolume(volume); renderer->SetBackground(1.0, 1.0, 1.0); // 设置背景颜色 // 启动交互器 interactor->Initialize(); interactor->Start(); return 0; } ``` 请将代码中的`"DICOM文件夹路径"`替换为实际的DICOM数据所在的文件夹路径,并确保已经正确安装和配置了VTK库。代码中的透明度函数和颜色函数是根据具需求进行设置,可以根据实际情况进行调整。运行该代码将显示一个包含绘制的渲染窗口,可以使用鼠标进行交互,可以使用鼠标滚轮进行缩放和旋转。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值