體繪製RayCasting(光線投影演算法)C++/OpenGL原始碼
RayCasting(光線投影演算法)C++原始碼
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include <GL/glut.h>
#define EPSILON 0.000001
#define WIDTH 400
#define HEIGTH 500
float Image[WIDTH*HEIGTH*4];
void GenerateVolume(int *Data,int* Dim);//生成體資料
void GenCube(int x,int y,int z,int side,int density, int *Data,int *Dim);//生成正方體資料
void GenSphere(int x,int y,int z,int radius,int density,int *Data,int *Dim);//生成球體資料
void Classify(float* CData,int *Data,int *Dim);//資料分類
void RotationMatrix(float *R,float *eye,float *center,float *up);//求取影象空間到物體空間變換的旋轉矩陣
void Composite(float *rgba,int x0,int y0,float *CData,int *Dim,float *R,float *T);//合成畫素顏色值
bool Intersection(float *startpos, float *pos, float *dir,int *Dim);//求光線與包圍盒交點座標
void TrInterpolation(float *rgba,float *pos,float *CData,int *Dim);//三線性插值
bool CheckinBox(float *point,int *Dim);//判斷點是否在包圍盒內
void MatrixmulVec(float *c,float *a,float *b);//矩陣向量乘積
void CrossProd(float *c,float *a,float *b);//向量叉乘
void Normalize(float *norm,float *a);//向量歸一化
void Mydisplay();//顯示影象
int main(int argc,char **argv)
{
int Dim[3]={200,200,200};//體資料大小
int *Data=(int *)malloc(sizeof(int)*Dim[0]*Dim[1]*Dim[2]);
float *CData=(float*)malloc(sizeof(float)*Dim[0]*Dim[1]*Dim[2]*4);
float R[9];//旋轉矩陣
float T[3]={0,0,450};//平移向量(要根據R調整,以保證獲得體資料全貌)
float eye[3]={0.5,0.5,1};//視點位置
float center[3]={0,0,0};//物體參考點位置
float up[3]={0,1,0};//相機朝上的方向
RotationMatrix(R,eye,center,up);//獲得旋轉矩陣
GenerateVolume(Data,Dim);//生成原始體資料
Classify(CData,Data,Dim);//對體資料分類
float *LinePS=Image;
for(int j=0;j<HEIGTH;j++)//逐個合成畫素值
{
for(int i=0;i<WIDTH;i++)
{
Composite(LinePS,i,j,CData,Dim,R,T);
LinePS+=4;
}
}
free(Data);
free(CData);
//使用OpenGL顯示此二維影象
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
glutInitWindowSize(500,500);
glutInitWindowPosition(200,200);
glutCreateWindow("Ray-Casting");
glClearColor (1,1,1,1);//背景設為白色
glutDisplayFunc(Mydisplay);//顯示影象
glutMainLoop();
}
//生成體資料
//********************************************************************//
//生成一個大正方體,內部包含一個球體,球體中間又包含一個小正方體
//Data:體積資料
//Dim:體資料大小
//********************************************************************//
void GenerateVolume(int *Data,int *Dim)
{
GenCube(0,0,0,200,100,Data,Dim);//大正方體
GenSphere(100,100,100,80,200,Data,Dim);//球體
GenCube(70,70,70,60,300,Data,Dim);//小正方體
}
//生成正方體資料
//********************************************************************//
//x,y,z:正方體左下角座標
//side:正方體邊長
//density:正方體對應的標量值
//Data:體積資料
//Dim:體資料大小
//********************************************************************//
void GenCube(int x,int y,int z,int side,int density, int *Data,int *Dim)
{
int max_x=x+side,max_y=y+side,max_z=z+side;
int Dimxy=Dim[0]*Dim[1];
for(int k=z;k<max_z;k++)
{
for(int j=y;j<max_y;j++)
{
for(int i=x;i<max_x;i++)
{
Data[k*Dimxy+j*Dim[0]+i]=density;
}
}
}
}
//生成球體資料
//********************************************************************//
//x,y,z:球心座標
//radius:球半徑
//density:球體對應的標量值
//Data:體積資料
//Dim:體資料大小
//********************************************************************//
void GenSphere(int x,int y,int z,int radius,int density,int *Data,int *Dim)
{
int radius2=radius*radius;
int Dimxy=Dim[0]*Dim[1];
for(int k=0;k<Dim[2];k++)
{
for(int j=0;j<Dim[1];j++)
{
for(int i=0;i<Dim[0];i++)
{
if((i-x)*(i-x)+(j-y)*(j-y)+(k-z)*(k-z)<=radius2)
{
Data[k*Dimxy+j*Dim[0]+i]=density;
}
}
}
}
}
//資料分類
//********************************************************************//
//將原始體資料的標量值對映為顏色和不透明度
//這裡處理的比較簡單,直接將之前生成的資料分三類:大正方體白色、球體紅色、小正方體黃色
//CData:分類後體資料
//Data:原始體資料
//Dim:體資料大小
//********************************************************************//
void Classify(float* CData,int *Data,int *Dim)
{
int *LinePS=Data;
float *LinePD=CData;
for(int k=0;k<Dim[2];k++)
{
for(int j=0;j<Dim[1];j++)
{
for(int i=0;i<Dim[0];i++)
{
if(LinePS[0]<=100)
{
//白色
LinePD[0]=1.0;
LinePD[1]=1.0;
LinePD[2]=1.0;
LinePD[3]=0.005;
}
else if(LinePS[0]<=200)
{
//紅色
LinePD[0]=1.0;
LinePD[1]=0.0;
LinePD[2]=0.0;
LinePD[3]=0.015;
}
else
{
//黃色
LinePD[0]=1.0;
LinePD[1]=1.0;
LinePD[2]=0.0;
LinePD[3]=0.02;
}
LinePS++;
LinePD+=4;
}
}
}
}
//求取從影象空間到物體空間變換的旋轉矩陣
//********************************************************************//
//功能類似於OpenGL中的gluLookAt函式
//參考:http://blog.csdn.net/popy007/article/details/5120158
//R:旋轉矩陣
//eye:視點位置
//center:物體參考點位置
//up:相機朝上的方向
//********************************************************************//
void RotationMatrix(float *R,float *eye,float *center,float *up)
{
float XX[3],YY[3],ZZ[3];//影象空間的基向量
ZZ[0]=eye[0]-center[0];
ZZ[1]=eye[1]-center[1];
ZZ[2]=eye[2]-center[2];
CrossProd(XX,up,ZZ);
CrossProd(YY,ZZ,XX);
Normalize(XX,XX);
Normalize(YY,YY);
Normalize(ZZ,ZZ);
//由影象空間基向量構成旋轉矩陣
R[0]=XX[0];R[1]=YY[0];R[2]=ZZ[0];
R[3]=XX[1];R[4]=YY[1];R[5]=ZZ[1];
R[6]=XX[2];R[7]=YY[2];R[8]=ZZ[2];
}
//合成畫素值
//********************************************************************//
//rgba:合成顏色值
//x0,y0:二維影象畫素座標
//CData:分類後體資料
//Dim:體資料大小
//R:旋轉矩陣(換用於影象空間到物體空間的轉換)
//T:平移向量(同上)
//********************************************************************//
void Composite(float *rgba,int x0,int y0,float *CData,int *Dim,float *R,float *T)
{
int stepsize=1;//取樣步長
float cumcolor[4];//累計顏色值
cumcolor[0]=cumcolor[1]=cumcolor[2]=cumcolor[3]=0.0;
float pos[3],dir[3];//投射光線起點、方向
float startpos[3];//光線與包圍盒近視點處的交點座標
float samplepos[3];//取樣點座標
float samplecolor[4];//取樣點顏色
//採用平行投影,故在影象空間中投射光線的方向(0,0,-1),起點(x0,y0,0)
pos[0]=x0;pos[1]=y0;pos[2]=0;
//將光線描述轉換到物體空間
//*********************************//
dir[0]=-R[2];dir[1]=-R[5];dir[2]=-R[8];//光線方向在物體空間的表達
MatrixmulVec(pos,R,pos);//旋轉
pos[0]+=T[0];//平移
pos[1]+=T[1];
pos[2]+=T[2];
//*********************************//
if(Intersection(startpos,pos,dir,Dim))//判斷光線與包圍盒是否相交
{
samplepos[0]=startpos[0];
samplepos[1]=startpos[1];
samplepos[2]=startpos[2];
while(CheckinBox(samplepos,Dim)&&cumcolor[3]<1)//當光線射出包圍盒或累計不透明度超過1時中止合成
{
TrInterpolation(samplecolor,samplepos,CData,Dim);//三線性插值獲得取樣點處的顏色及不透明度
//合成顏色及不透明度,採用的是從前到後的合成公式
cumcolor[0] +=samplecolor[0]*samplecolor[3]*(1-cumcolor[3]);//R
cumcolor[1] +=samplecolor[1]*samplecolor[3]*(1-cumcolor[3]);//G
cumcolor[2] +=samplecolor[2]*samplecolor[3]*(1-cumcolor[3]);//B
cumcolor[3] +=samplecolor[3]*(1-cumcolor[3]); //A
//下一個取樣點
samplepos[0]+=dir[0]*stepsize;
samplepos[1]+=dir[1]*stepsize;
samplepos[2]+=dir[2]*stepsize;
}
rgba[0]=cumcolor[0];
rgba[1]=cumcolor[1];
rgba[2]=cumcolor[2];
rgba[3]=cumcolor[3];
return;
}
rgba[0]=rgba[1]=rgba[2]=rgba[3]=1.0;//若光線與包圍盒不相交,賦白色
}
//判斷投射光線與包圍盒是否相交(若相交,求靠近視點處的交點座標)
//********************************************************************//
//思路:將包圍盒6個面無限擴充套件,並分成3組,即平行於XOY,YOZ,ZOX平面的各有2個;
//求光線與6個平面的交點,從每組的2個交點中選出距離視點較近者,這樣得到3個候
//選交點;從這3個候選交點中選出距離視點最遠的那個。最後判斷這個點是否落在包
//圍盒內,若在,即為光線與包圍盒的靠近視點處的交點。
//stratpos:靠近視點處的交點座標
//pos:光線起點座標
//dir:光線方向向量
//Dim:包圍盒右上角座標(左下角座標為(0,0,0))
//********************************************************************//
bool Intersection(float *startpos, float *pos, float *dir,int *Dim)
{
float nearscale = -1000000;
float scale1, scale2;
//光線與包圍盒平行於YOZ的2個平面交點
if ((dir[0] <=-EPSILON)||(dir[0] >=EPSILON))
{
scale1 = (0- pos[0]) / dir[0];
scale2 = (Dim[0] -1 - pos[0]) / dir[0];
//選出靠近視點的交點,並與當前候選點比較,保留較遠者
if (scale1 < scale2)
{
if (scale1 > nearscale)
nearscale = scale1;
}
else
{
if (scale2 > nearscale)
nearscale = scale2;
}
}
//光線與包圍盒平行於ZOX的2個平面交點
if ((dir[1] <=-EPSILON)||(dir[1] >=EPSILON))
{
scale1 = (0 - pos[1]) / dir[1];
scale2 = (Dim[1] -1 - pos[1]) / dir[1];
//選出靠近視點的交點,並與當前候選點比較,保留較遠者
if (scale1 < scale2)
{
if (scale1 > nearscale)
nearscale = scale1;
}
else
{
if (scale2 > nearscale)
nearscale = scale2;
}
}
//光線與包圍盒平行於XOY的2個平面交點
if ((dir[2] <=-EPSILON)||(dir[2] >=EPSILON))
{
scale1 = (0 - pos[2]) / dir[2];
scale2 = (Dim[2] -1 - pos[2]) / dir[2];
//選出靠近視點的交點,並與當前候選點比較,保留較遠者
if (scale1 < scale2)
{
if (scale1 > nearscale)
nearscale = scale1;
}
else
{
if (scale2 > nearscale)
nearscale = scale2;
}
}
startpos[0] = pos[0] + nearscale * dir[0] ;
startpos[1] = pos[1] + nearscale * dir[1] ;
startpos[2] = pos[2] + nearscale * dir[2] ;
return CheckinBox(startpos,Dim); //判斷該點是否在包圍盒內
}
//三線性插值
//********************************************************************//
//rgba:插值結果
//pos:取樣點座標
//CData:分類後體資料
//Dim:體資料大小
//********************************************************************//
void TrInterpolation(float *rgba ,float *pos,float *CData,int *Dim)
{
int x0,y0,z0,x1,y1,z1;
float fx,fy,fz;
float v0,v1,v2,v3,v4,v5,v6;
int Slicesize=Dim[0]*Dim[1]*4;
int Stepsize=Dim[0]*4;
x0=(int)pos[0];//整數部分
y0=(int)pos[1];
z0=(int)pos[2];
fx=pos[0]-x0;//小數部分
fy=pos[1]-y0;
fz=pos[2]-z0;
x1=x0+1;
y1=y0+1;
z1=z0+1;
if(x1>=Dim[0])x1=Dim[0]-1;//防止越界
if(y1>=Dim[1])y1=Dim[1]-1;
if(z1>=Dim[2])z1=Dim[2]-1;
for(int i=0;i<4;i++)
{
//取樣點處的值由鄰近的8個點插值獲得
v0=CData[z0*Slicesize+y0*Stepsize+4*x0+i]*(1-fx)+CData[z0*Slicesize+y0*Stepsize+4*x1+i]*fx;
v1=CData[z0*Slicesize+y1*Stepsize+4*x0+i]*(1-fx)+CData[z0*Slicesize+y1*Stepsize+4*x1+i]*fx;
v2=CData[z1*Slicesize+y0*Stepsize+4*x0+i]*(1-fx)+CData[z1*Slicesize+y0*Stepsize+4*x1+i]*fx;
v3=CData[z1*Slicesize+y1*Stepsize+4*x0+i]*(1-fx)+CData[z1*Slicesize+y1*Stepsize+4*x1+i]*fx;
v4=v0*(1-fy)+v1*fy;
v5=v2*(1-fy)+v3*fy;
v6=v4*(1-fz)+v5*fz;
if(v6>1)v6=1;//防止越界
rgba[i]=v6;
}
}
//判斷點是否在包圍盒內
//********************************************************************//
//point:點座標
//Dim:包圍盒右上角座標(左下角座標為(0,0,0))
//********************************************************************//
bool CheckinBox(float *point,int *Dim)
{
if (point[0] < 0||point[0] >= Dim[0]||point[1] < 0||point[1] >= Dim[1]||point[2] < 0||point[2] >= Dim[2])
return false;
else
return true;
}
//矩陣與向量乘積
//********************************************************************//
//c=a*b
//c:輸出向量
//a:輸入矩陣
//b:輸入向量
//********************************************************************//
void MatrixmulVec(float *c,float *a,float *b)
{
float x,y,z;
x=a[0]*b[0]+a[1]*b[1]+a[2]*b[2];
y=a[3]*b[0]+a[4]*b[1]+a[5]*b[2];
z=a[6]*b[0]+a[7]*b[1]+a[8]*b[2];
c[0]=x;
c[1]=y;
c[2]=z;
}
//向量叉乘
//********************************************************************//
//c=a x b
//c:輸出向量
//a:輸入向量
//b:輸入向量
//********************************************************************//
void CrossProd(float *c,float *a,float *b)
{
float x,y,z;
x=a[1]*b[2]-b[1]*a[2];
y=a[2]*b[0]-b[2]*a[0];
z=a[0]*b[1]-b[0]*a[1];
c[0]=x;
c[1]=y;
c[2]=z;
}
//向量歸一化
//********************************************************************//
//norm:歸一化結果
//a:輸入向量
//********************************************************************//
void Normalize(float *norm,float *a)
{
float len=sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2]);
norm[0]=a[0]/len;
norm[1]=a[1]/len;
norm[2]=a[2]/len;
}
//顯示函式
void Mydisplay()
{
glClear(GL_COLOR_BUFFER_BIT);
glDrawPixels(WIDTH,HEIGTH,GL_RGBA,GL_FLOAT,Image);//使用OpenGL的繪圖函式
glFlush();
}
相關文章
- 使用opengl繪製yuv
- 用Python繪製移動均線【含原始碼】Python原始碼
- OpenGL 繪製你的 github skyline 模型Github模型
- View繪製流程原始碼分析View原始碼
- 用Python繪製專業的K線圖【含原始碼】Python原始碼
- Android日常學習:OpenGL 實踐之貝塞爾曲線繪製Android
- QT OPENGL 與 shader 繪製展示視訊程式碼例項 OPenGL直接顯示YUV資料QT
- YYAsyncLayer 原始碼剖析:非同步繪製原始碼非同步
- Android View繪製原始碼分析 MeasureAndroidView原始碼
- [OpenGL ES] 正交投影
- OpenGL 學習系列--基礎的繪製流程
- OpenGL 學習系列---基本形狀的繪製
- Android OpenGL ES 開發(二):繪製圖形Android
- 燈光系統圖繪製
- 短視訊原始碼,在Android 中opengl es實現燈光效果原始碼Android
- Android原始碼分析之View繪製流程Android原始碼View
- RecyclerView 原始碼分析(一) —— 繪製流程解析View原始碼
- C++ Qt開發:Charts折線圖繪製詳解C++QT
- OpenGL 之 GPUImage 原始碼分析GPUUI原始碼
- YYText 原始碼剖析:CoreText 與非同步繪製原始碼非同步
- canvas繪製拋物線程式碼例項Canvas線程
- canvas繪製直線Canvas
- SVG 繪製直線SVG
- canvas 繪製線條Canvas
- OpenGL 繪圖移動繪圖
- 基於原始碼分析 Android View 繪製機制原始碼AndroidView
- Android OpenGL ES 2.0 手把手教學(5)- 繪製模式Android模式
- 學習OpenGL ES之繪製三角形
- Android系統原始碼分析--View繪製流程之-inflateAndroid原始碼View
- Android系統原始碼分析–View繪製流程之-setContentViewAndroid原始碼View
- Android系統原始碼分析--View繪製流程之-setContentViewAndroid原始碼View
- Android View 原始碼解析(三) – View的繪製過程AndroidView原始碼
- 如何繪製一個類甘特圖 (附原始碼)原始碼
- Flutter 繪製動機 VSYNC 流程原始碼全方位分析Flutter原始碼
- SVG <polyline> 繪製折線SVG
- canvas 繪製雙線技巧Canvas
- PyQtGraph繪製折線圖QT
- AnyChart繪製折線圖
- Matplotlib 繪製折線圖