CUDA Volume Rendering [Part.4 Render(GPU part)]


前面已經把 CPU 要做的事都講完了,接下來,就是最重要的 GPU 的 Kernel 程式了!這份 kernel 的定義如下:

__global__ void
d_render(uint *d_output, uint imageW, uint imageH,
         float density, float brightness,
         float transferOffset, float transferScale)

他要接受的參數有:

  • d_output
    儲存輸出結果的 1D array,對應到 pixel buffer object

  • imageW, imageH
    輸出影像的大小,也就是 d_output 的大小

  • density
    調整每一點顏色的透明度的比例

  • brightness
    最後結果的亮度調整比例

  • transferOffset , transferScale
    把 volume 資料對應到 transfer function 的調整參數

而 Heresy 把這個 kernel 程式的動作,分成四大部分:

  1. 設定前置參數、計算 index 和視線在空間中的位置
  2. 計算視線和 volume 的外框相交的點和攝影機的距離
  3. 由後往前,計算視線上的顏色
  4. 將結果寫入到 pixel buffer object

第一部分就是設定一些需要的變數了~這裡包含了 maxStepststepboxMinboxMax 四個變數,Heresy 會在用到的時候再去解釋。而接下來,就是根據 thread index 和 block index,計算出這個 thread 的索引值了~這邊還是很單純的,用 index = (block index) * (block size) + (thread index) 來做計算。

uint x = __umul24(blockIdx.x, blockDim.x) + threadIdx.x;
uint y = __umul24(blockIdx.y, blockDim.y) + threadIdx.y;

不過,這邊他改用 24bit 的整數乘法函式 __umul24() 來取代 32bit 的 operator*;在目前的硬體上,這樣是會比較快一點的。

算出來的 xy,就是代表現在這個點的結果,是要儲存在輸出畫面的哪一點上;而要開始計算這一點的顏色,還要再把他轉換成空間座標的一條線。而這邊就要先講一下這個範例程式的空間定義了~首先,他把 volume 視為一個 (-1, -1, -1) 到 (1, 1, 1) 的一個正方體,並把這兩個座標儲存在 boxMinboxMax。而攝影機的位置在預設會有一個 z 方向的位移 4,所以位置會是 (0, 0, 4);而視平面則是建立在相對攝影機的 (0,0,-2) 的位置,大小是 2*2。而在旋轉時,實際上都是將攝影機的位置做移動,不會動到代表 volume 的 box。

整個空間關係,可以畫成下方的示意圖。

viewport

實際上在程式的部分,是寫成下面的樣子:

float u = (x / (float) imageW)*2.0f-1.0f;
float v = (y / (float) imageH)*2.0f-1.0f;

// calculate eye ray in world space
Ray eyeRay;
eyeRay.o = make_float3(mul(c_invViewMatrix, make_float4(0.0f, 0.0f, 0.0f, 1.0f)));
eyeRay.d = normalize(make_float3(u, v, -2.0f));
eyeRay.d = mul(c_invViewMatrix, eyeRay.d);

第一段在計算 u, v 的值,就是在把他們由 [0, imageW] 和 [0, imageH] 轉換到 [-1, 1] 的座標定義;而 eyeRay 就是用來儲存攝影機的位置(eyeRay.o)和這條視線所看的方向(eyeRay.d)。eyeRay.o 就是直接帶入矩陣 c_invViewMatrix;而 eyeRay.d 則是根據計算出的 u, v,再考慮視平面 z 軸的位移 -2,建立出長度為 1 的向量,最後再考慮矩陣 c_invViewMatrix。如此,就可以建立出符合上述空間定義的資料了。

而接下來第二部分, 就是要計算 eyeRay 這條視線,和 volume 的 box 的交點了。在這邊,他是呼叫這個 intersectBox() 函式;傳入 eyeRayboxMinboxMax 後,他會傳回這條視線是否有和 volume 相交,同時把和 volume 相交點到攝影機位置距離的最小值和最大值記錄在 tneartfar 中。而 tneartfar 也就代表了這個 volume 在這條視線上,對攝影機來說對短距離與最遠距離;這是之後用來計算 ray casting 要走過哪些區域的依據。這部分的程式如下:

// find intersection with box
float tnear, tfar;
    int hit = intersectBox(eyeRay, boxMin, boxMax, &tnear, &tfar);
if (!hit) return;
    if (tnear < 0.0f) tnear = 0.0f;     // clamp to near plane

而這邊 intersectBox() 用的計算方法,算是 Heresy 覺得相當有趣的一個方法,他是參考 Siggraph 的一篇 education 來做的;基本上,就是透過將 box 限制在和座標軸同方向,來簡化計算。在這個方法裡,他可以簡單的向量運算,就快速的求出一條射線和六個平面焦點對於某個點的距離。不過,在這邊細節就先不提了。

再來的第三部分,就是實際要計算 volume 在 (x, y) 這點的顏色的部分了!這段的程式碼如下:

// march along ray from back to front, accumulating color
float4 sum = make_float4(0.0f);;
float t = tfar;
for(int i=0; i<maxSteps; i++) {		
    float3 pos = eyeRay.o + eyeRay.d*t;
    pos = pos*0.5f+0.5f;    // map position to [0, 1] coordinates

    // read from 3D texture
    float sample = tex3D(tex, pos.x, pos.y, pos.z);

    // lookup in transfer function texture
    float4 col = tex1D(transferTex, (sample-transferOffset)*transferScale);

    // accumulate result
    sum = lerp(sum, col, col.w*density);

    t -= tstep;
    if (t < tnear) break;
}
sum *= brightness;

基本上,他的計算方法就是由一條視線和 volume 相交最遠的點(距離為 tfar),以 tstep 為間距向攝影機前進,直到和攝影機的距離小於 tnear,或累積次數大於 maxSteps。每前進一步,都可以透過「pos = eyeRay.o + eyeRay.d*t」計算出在空間座標中的位置;不過由於這是計算一個在 volume box 中的點,所以他的座標會是介於 (-1,-1,-1) 和 (1,1,1) 之間;而由於 texture 的 normailized 座標是介於 [0, 1] 之間,所以必須再把他做個轉換。

而接下來,就是透過 tex3D() 到 volume data 的 3D texture(tex)去取得 (pos.x, pos.y, pos.z) 這一點的值 sample。而這裡取出來的值會是灰階的,要再用 tex1D() 到代表 transfer function 的 1D texture transferTex 中,去找到對應的色彩;不過,這邊會先把取得的 sample 透過 transferOffsettransferScale 做一個調整。

取得這一點的色彩後,接下來就是要和目前的結果 sum 做累加的動作;在這邊,他是用一個定義在 cutil_math.h 裡的函式 lerp() 來進行這項計算。而「sum = lerp(sum, col, col.w*density);」實際的動作,就是:「sum += (col.w * density) * (col – sum);」;其中,density 這個參數算是在調整每一點的 alpha 值了~

而最後,就是計算計數器 t 的值,並確認他還沒有超過 tnear;如果已經超過的話,就提前結束整個迴圈。而最後,他還會再把最後的顏色,根據 brightness 做一的調整,然後才是最後的 (x, y) 這個點的顏色 sum

最後的第四部分,就是將計算出來的顏色值 sum,寫入到代表 pixel buffer object 的 d_output 裡了。而他的第一個動作,就是確認 (x, y) 的質是否有超過 (imageW, imageH)(其實這個動作應該可以更早做?);之後,就是計算出 (x, y) 在一維陣列中 d_output 中的索引值 i,並把值寫進去。

不過,由於 d_output 的型別是 uint*,而 sumfloat4,所以這篇他是先透過 rgbaFloatToInt() 來進行轉換,把 float4 的色彩值用一個 unsigned int 來儲存,再存到 d_output

這段程式的內容如下:

if ((x < imageW) && (y < imageH)) {
    // write output color
    uint i = __umul24(y, imageW) + x;
    d_output[i] = rgbaFloatToInt(sum);
}

而到此,也就是 CUDA Volume Rendering 的 kernel 的全部內容了~而 Heresy 關於 CUDA 提供的這個 Volume Render 範例,大概也就先講到這了~ :)


對「CUDA Volume Rendering [Part.4 Render(GPU part)]」的想法

  1. 您好,感谢您的一系列文章!
    我有一个问题。我的工程项目里需要需要通过设定硬阈值的方式将不透明物体进行渲染。我基于例程改动了一下,将灰度图彩色化的部分去掉,仅使用灰度图作为素材。而又将光线照射到box后的过程进行了小的修改。
    问题是,现在渲染出来的图片只有外围,物体内部信息丢失了,但是按照我设定的硬阈值是不该这样的呀?
    没有贴具体程序可能有些糊涂,但不知道您能否感觉出我这个思路有什么问题吗?
    谢谢!

    • 以你提供的資訊,無法做任何判斷。
      這邊只能建議你試試看不要一次改動多個地方,一次只改一處(例如只改閥值),來確認是哪邊的問題。

  2. Heresy哥
    感謝你的文章!!對於初學者的我讓我學習到很多!!我有幾個問題想要請問一下
    就是為什麼我用自己的raw檔進行體繪製!!為什麼畫出來的東西不是跟數據裡面一樣呢?然後畫出來就是一個紫色的方塊!讀入raw的時候是不是要修改哪裡的數據阿?(我是用cuda4.0的sample檔

    • 如果你是直接永他的程式的話,可能要先確認,你的 RAW 檔的資料,可以用他的方法來做讀取,而讀進來的資料也是符合預期的。

      • Heresy 大大
        可以請教一下要改哪邊嗎?
        因為我改了好久!還是沒有成功!不知道是不是有看錯哪裡!!一直修改不成功!!

          • 不好意思!請問一下!我畫出來的圖有問題!有可能是色彩的對應的地方有問題嗎?因為我剛剛查了我讀進去的bytes是對的!但是出來的圖就跟檔案的圖差很多!

          • 當然也是有可能的。
            Volume rendering 的 transfer function 的設定,會改變最後呈現的結果,如果設定的不對,是有可能完全看不到自己想要看的東西的。

  3. 【你應該是有確定 Camera 位置、角度的正確性,不過有沒有可能是 FOV 不一致呢?】
    我不是很确定你说的FOV是什么意思,如果我没理解错,FOV是指gluPerspective的第一个参数,但我感觉在光线投射中没有涉及到这个参数的使用。

    因为我从一开始就使eye Position是由camera的位置转换而来,这个转换是减去 (boxMax+boxMin)/2)再除以 (boxMax-boxMin)/2形成的(这个转换能将代理几何体转换为(-1,-1,-1)~(1,1,1))。而eye Position与屏幕中心的连线形成的向量直接就是camera的方向,最后eye Position与屏幕的距离为zNear(即初始都在Z轴时,取eyeRay.o 为(0,0,6),那么屏幕的坐标为(u,v,6-zNear))。

    所以在整个ray casting过程中我并没有发现哪个地方可以使用到透视投影中的FOV参数。但考虑我的ray casting结果是比实际的代理几何体大,貌似FOV也是很可能导致这个结果。那么请问您这个FOV参数如何体现在ray casting的整个过程中呢?

    • FOV 是 field of view,基本上就是可視角的大小,一般是用角度來表示。
      而在 ray casting 或 ray tracing 的時候,FOV 基本上不是像使用 gluPerspective() 時是直接指定,而是要自己透過產生的 ray 的範圍來做控制的。
      在這個例子裡,可以想像成就是在攝影機和物體都不動的情況下,去修改可視區域(view plane)的大小。

      • 谢谢您的指点,这个我大概明白了。ray-casing的结果比较大是因为在ray-casting的时候我设置的屏幕范围是(-1,-1)~(1,1),刚好与代理几何体的一个面一样大,而在实际场景中屏幕与代理几何体不一定是这样的对应关系。我再仔细修改看看,Thanks for your help!!!!

      • Heresy:其实我觉得如果单单是ray-casting的结果比较大造成的FOV比例不符是不难解决,我只要在坐标转换时除以t2 = (boxMax+boxMin)/2时多除一点就可以缩小ray-casting的结果了。但我有一个更为诡异的问题:当camera方向为(0,y,z)的时候,我上下移动camera(即pitch操作,按住鼠标上下移camera的方向,camera位置不变),此时ray-casting的结果是随着实际的代理几何体正常的上下移动。但当camera方向向量的x分量不为0时,如果我再上下移动camera,ray-casting的结果似乎会上下移动的同时也绕camera方向旋转,像螺旋形那样。

        我分析了很久也不知道这个问题的原因是什么,感觉上应该是矩阵变换过后屏幕歪了,即屏幕绕着camera方向旋转了一下,但我的整个矩阵变换机制都没有包含绕camera方向的旋转变换,不知道您觉得这个原因是什么?有没有类似比较方便在3D坐标系输入坐标画点线的软件呢?我想验证下屏幕是不是歪了,但没有找到比较方便的方法。谢谢您的解答。

        • 由於你是要用 2-pass rendering,個人會建議,不要用「多除一點」這樣的試誤法來找他的 scaling,而是要實際推導、算出他該有的大小,這樣比較不會有問題。

          而攝影機控制的問題,感覺有幾種可能,一個是你的旋轉矩陣在套用時應該有那裡有弄錯(例如矩陣套用的順序)。
          另一個可能,則是你忽略的座標系統的問題。在這邊這個 ray casting 的例子裡,整個座標系統是經過轉換的,根據狀況的不同,甚至有可能會有 x / y / z 軸的單位長度定義不同的狀況;如果沒有考慮這點,直接在這個座標系統套用一般座標系統的轉換矩陣,是有可能有問題的。

          另,要畫出 3D 的點和線,你正在用的 OpenGL 不就可以了嗎?

  4. It’s me again! 按照上次您指导的思路,将光线投射的结果纹理合成到当前场景中,通过自己的尝试,思路很清晰了实现也做了,但效果没有出来,还是有一些问题,尤其是里面视点关系和坐标转换,我不确定自己对不对,想再请教一下您:

    我自己的3D场景中,有摄像机类,其中包含视点在世界坐标的位置float3 eyePos和视点方向float3 eyeDirection,也就是用于设定glLookAt的前两个参数。还有世界坐标下的包围盒,通过float3 boxMin和float3 boxMax表示,和CUDA render kernel函数中的那两个参数意义一样。

    此后我想将此包围盒归一化到CUDA render kernel中一样的(-1,-1,-1)–(1,1,1)。其实就是一个平移缩放变换,即float3 t1 = (boxMax+boxMin)/2,float3 t2 = (boxMax-boxMin)/2,包围盒坐标减去t1再除以t2就能归一化到(-1,-1,-1)–(1,1,1)中。所以,我将eyePos做此变换,eyePos=(eyePos-t1)/t2。此时的eyePos成了在您上文图中那个坐标系中视点的坐标,我将eyePos赋给kernel 函数中的eyeRay.o。

    然后,我考虑到在CUDA kernel中,(u,v,z)实际上表示的是屏幕上某一点的坐标,且此时的屏幕是垂直于Z轴,而z坐标可以通过eyeRay.z – 视点与屏幕的距离(即gluPerspetive中的zNear参数)得到。而且此时视线的方向为(0,0,-1),而在世界坐标中视线的方向我已经知道视线方向是eyeDirection,向量在平移缩放是不变的对吧,所以我求得(0,0,-1)到eyeDirection的旋转矩阵M(按照display函数中的方法,viewRotation就是旋转(0,0,-1)到eyeDirection的各个角度,而viewTranslation记录了在世界坐标下移动距离之后也进行了减t1除以t2变换)。我将矩阵M乘以屏幕上的每一点(u,v,z),得到了此时屏幕上每点在您图中坐标系下的真实位置float3 temp。据此,我可以得出光线的方向eyeRay.d = temp – eyeRay.o再normalize

    然后之后的光线投射步骤都一样,但我并没有得到结果。想请问您我这个将真实世界坐标下视线的方向位置转换为CUDA kernel中坐标系光线方向与位置的过程对么???如果您需要,我可以给出代码。

      • Heresy 您好。经过两天的仔细排查,我按照我上面所说的步骤求得了最后的转换矩阵,并带入了多个点的坐标发现转换完全是正确的。但问题或许不是矩阵,而是kernel函数中tex3D总是返回0所以一直是黑色。我有两个版本的程序,第一个是在此SDK基础上修改,使用自己的数据进行体绘制,完全正常。第二个是配合OpenGL将光线投射结果加入了真实的三维场景,CUDA部分的内容和第一个版本完全一样,但就是tex3D无论是什么坐标位置总是返回0。我觉得这应该是3D纹理未绑定上的缘故,但我通过拷贝回host端可以确定我用于绑定3D纹理的数据是有效的,而且绑定过程也是和这个SDK的过程完全一样。请问您觉得还可能是什么样的原因呢?整个程序未见任何报错,会不会是在使用OpenGL绘制场景时使用了很多纹理而导致纹理空间不够的缘故呢?

        • 所以你現在的問題,是同樣的程式,再加上 OpenGL Render 的部分後,就不能用了?
          而且同樣的數據,會變成在 CUDA Kernel 裡面讀不到資料?
          那如果把這個程式裡面 OpenGL 相關的部分註解掉、讓他不去執行呢?

          不管是 CUDA 或是 OpenGL,在 texture 上的確都有一些限制,但是除非你用的真的很多,不然應該不會踩到地雷才對。

          個人會建議,如果是 CUDA 程式的問題,試試看使用 nsight 來做偵錯吧
          https://kheresy.wordpress.com/2012/05/16/nvidia-cuda-5-preview-and-nsight-eclipse-edition-kepler-tesla/
          透過他的偵錯功能,應該可以比較容易找到問題。

          如果是 OpenGL 的問題,則可以試試看用 gdebugger
          https://kheresy.wordpress.com/2011/07/04/amd-gdebugger/

          • 经过几天的排查,我想我是犯了一个严重的错误–在绑定CUDA纹理之后才调用选择CUDA设备的函数,改正之后已经可以将体绘制的结果显示到三维场景中了。

            不过小问题依然有一点,当场景中的代理几何体移动时(包括移动代理几何体的实际世界坐标和移动摄像机时等价于代理几何体在视口中反向移动),体绘制的结果大概是在代理几何体的位置,也能随着代理几何体移动。但问题有两个:1.体绘制的结果明显比实际代理几何体大;2.体绘制结果比实际代理几何体移动的快。

            我想请问您的就是,按照我前面所述的思路(实际场景中摄像机位置通过矩阵变换对应您那副图中eye Position的位置,而摄像机看的方向通过矩阵变换对应您那幅图中eye Postion到屏幕中心点的方向。默认屏幕位置就对应的是近裁剪面位置)。

            那么此时光线投射绘制的整个过程是不是相当于一次实际的透视投影呢?因为我觉得如果体绘制结果比实际代理几何体大,那么可能是体绘制和实际的透视投影过程是有不同的。但我感觉都是一个点然后发射很多光线,而我又把摄像机位置和方向对应起来了,两者应该是一样的才对吧?

            感觉说的比较绕口,不知道您是否清楚我的意思了?非常感谢您一直的指导。

          • 這邊的 Ray tracing 應該是透視投影沒有錯。

            以你的描述來看,Heresy 會覺得比較有可能是你兩邊的投影設定不一致所造成的。
            你應該是有確定 Camera 位置、角度的正確性,不過有沒有可能是 FOV 不一致呢?

  5. 嗯,我明白您前面说的意思。我可能没表述清楚,我现在的想法是这样的,您看看是可行的么:第一次通过光线投射只绘制体数据部分,第二次通过OpenGL绘制其他3D场景。两次绘制的屏幕摄像机都是一样的,所以最后直接合成,人为的将体绘制深度值设为最小使其挡在前面,加之前面在计算体数据的时候已经根据遮挡关系修改了体数据的值,这样就可以模拟出体数据被其他3D物体遮挡的感觉而不是通过做深度测试达到。

    所以我现在不知道怎么做的关键点就是:
    1. 将体绘制的结果PBO与OpenGL固定管线渲染的其他3D物体合成到一个场景中。您前面说【因為 volume 在畫完後,已經沒有深度的資訊了,所以沒辦法和其他 3D 物體做 depth test。】,我的理解是没办法对体绘制的结果和其他3D物体做每一点的深度测试表现遮挡关系,但我在计算体数据的时候已经判断了遮挡问题,最后只是想合成两次绘制的结果,这样应该是可行的吧?具体应该怎么实现呢?需要将OpenGL渲染其他3D物体的结果也弄成一个PBO么?
    2.将体绘制的结果PBO人为设定一个最小的深度值以方便其合成到整个场景的最前面,这样是可能的么?

    不好意思,我的图形学经验尚浅,这个问题已经摸索了一个星期,还是这两天在您的指导下有了一些思路,再次为我带来的这么多问题表示歉意,也十分感谢您的耐心指点。

    • 你想做的事情,應該就是 Computer Graphics 裡的 multi-pass rendering。

      如果你有控制好,用 CUDA Ray casting 的東西都在其他東西後面、或是不會重疊到的話,在這個例子裡,你基本上只要把 CUDA 畫出來的結果,當成背景先貼上去,然後再不清除 color 的情況下,繼續用本來的 pipeline 畫就可以了。

      另外,針對沒有深度的東西,其實把 depth test 關掉就好了。
      這樣會強制蓋過本來的東西。

      • 非常感谢您这几天回答,通过这几天与您的交流,思路也逐渐清晰,应该可以慢慢动手来实验了。By the way, 这两天大陆作家韩寒高度赞扬了台湾之行中台湾人友好宽厚乐于助人等优秀品质,在您这里,我有了深切体验,以前从来没有哪个作者能如此迅速又详细的回答陌生读者的问题,真的非常感动!希望明年能来宝岛旅游,祝您好运。O(∩_∩)O~。

  6. Heresy 你好,你的这篇文章给我带来了巨大的帮助,非常感谢。但现在我有些问题很困惑想请教你,和飞的问题有点类似。现在我的程序需要在三维空间中进行体绘制,数据值可以通过一定的模型在三维空间中的每个离散点得到。也就是说,这个体绘制的代理几何体是在空间中的任意位置而非这个程序中的(-1.-1.-1)到(1,1,1)之间,那么请问应该怎么将这个代理几何体表示到三维空间中并且移动呢?

    • 這邊的程式把物體的範圍限制在 (-1,-1,-1) 到 (1,1,1) 之間,只是為了加速計算而已;而實際上空間中的其他位置,也都可以透過一個 transfrom matrix 轉換過來,差別只是你要把哪裡當作圓點而已。

      如果不在乎計算複雜度的差異的話,也可以自己去修改他的計算方法,來完成空間中任意點的 ray casting,不見得一定要在 (-1,-1,-1) 到 (1,1,1) 之間。

      • Heresy,非常感谢你的回复。我重新理解了下你的讲解,觉得你说的没错。我想进一步确认一下:假设我现在在3D空间中的矩阵坐标为(0,0,0)~(128,128,128),那么我现在需将这个范围减64再除以64得到(-1,-1,-1)~(1,1,1)的范围没错吧。然后再根据gluLookAt以及gluPerspective设定的相关参数找到在3D空间的眼睛位置,视线方向和屏幕位置,同样减64再除以64得到CUDA在投射光线那个坐标系下光线的o和d。
        如果这一步我理解的没有问题没有问题,那么我还有两个疑惑:
        1. CUDA程序中是得到了d_output,d_output通过PBO绘制到屏幕上,而其他3D空间中的物体比如地形之类是通过OpenGL固定管线绘制,但是似乎我现在不是很清楚,如何让PBO绘制的这块东西刚好显示在我3D空间中指定的那个体绘制的位置,比如(0,0,0)~(128,128,128)?
        2. 还有一个有点弱智的工程性问题:我用的NeHe那个OpenGL框架,即windows SDK+OpenGL,但现在在调用cutilSafeCall( cudaGLSetGLDevice( 0 ) )时候总是运行返回 “cudaSafeCall() Runtime API error 36: cannot set while device is active in this process.”。我之前没有调用任何cuda的函数,甚至将这个调用放在main函数的第一行仍然是一样的问题,请问这是什么原因呢?

        真是很抱歉一下提了这么问题,麻烦您了。

        • 1. 基本上,在這邊的範例來說,所有繪製的動作,都已經在 CUDA 裡面用 RAY-casting 畫完了!
          最後得到的結果,就相當是一張最後螢幕要顯示的結果圖而已;位置的問題,已經在計算 camera 的相對位置的時候就解決掉了。
          而 OpenGL 部分在做的,就只是把這張圖直接畫出來在顯示區域上而已。

          由於他這邊是純粹考慮 ray casting 這個 volume 而已,所以並沒有辦法和其他 OpenGL pipeline 的東西做整合;因為 volume 在畫完後,已經沒有深度的資訊了,所以沒辦法和其他 3D 物體做 depth test。
          如果有需要這樣結合兩種不同的 render 方法的話,需要另外做處理。

          2. 抱歉,Heresy 沒用過 NeHe 的 framework。

          • 感谢您的耐心回复。
            前两天我的思路是想把volume render的结果去和其他3D物体交互,通过阅读您的文章,以及您下面和另外网友的对话,我也注意volume render的结果就是一张图片,不存在深度信息,直接与其他3D物体交互是不可能的。
            但我现在的想法有点不同,现在我的程序是在3D空间中存在一个代理立方体,立方体中均匀离散了很多点,每个点都有对应的值。于是我想在这个时候就做判断,比如,我会判断每个点y坐标与对应地形位置高度图的关系,如果该点比地形高度小,那么它的值置为0。当完成所有这种判断后,我再将这个立方体的值打包为3D纹理传入CUDA进行ray casting,得到的结果就是已经修正过的结果了。
            所以我希望的是,有没有可能将这个结果,虽然是一张图片,正好贴在3D场景中立方体的位置呢?
            再次感谢您的耐心指导。

          • 前面已經說了,理論上 camera 的計算都正確的話,你畫出來的圖裡面,物體就會在正確的位置。理論上設定一致、和場景中其他物體沒有遮蔽的問題的畫,只要直接畫出來就可以了。

發表留言

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料