现在的位置: 首页 > 综合 > 正文

体绘制

2013年12月07日 ⁄ 综合 ⁄ 共 4686字 ⁄ 字号 评论关闭

体绘制

体绘制中光线投射的主要步骤如下:

(1)计算光线上的点与体数据交点,并且将所有的交点进行累加

vec4 DirectVolumeRendering(vec3 front, vec3 back, float upperThreshold, float threshold, float offset)
{
	//1.0计算视点的位置和投影的方向
	vec3 dir = back - front;
	float len = length(dir);
	vec3 norm_dir = normalize(dir);
	vec4 result = vec4(0,0,0,0);//RGBA
	float raylength = 0;
	//2.0计算光线穿过体数据累加投影得到的RGBA值
	while(result.a < 1.0 && raylength < len )
	{
		//2.1得到光线在raylength长度处与体数据相交的位置pos
		vec3 pos = front+raylength*norm_dir;

		//2.2得到体数据中pos位置的灰度值
		float valuef = texture3D(blockPool, pos).a;

		//2.3根据灰度值,对照颜色表,得到对应的RGBA值
		vec4 texValue = texture1D(tranf, valuef);

		//2.4根据光线传输的距离,对透明值做加权处理
		texValue.a = texValue.a *stepsize *sizeX;

		//2.5根据pos位置处RGBA值,以及之前计算得到的result值,继续累加得到result
		result.rgb += (1.0-result.a)*texValue.a*texValue.rgb;
		result.a += (1.0-result.a)*texValue.a;

		//2.6光线长度继续向外延伸
		raylength +=  stepsize;
	}
	return result;
}

(2)添加阴影,如果光线上有一点的值大于阈值Outsideisovalue,则认为该光线需要添加阴影

float Shadow(vec3 pos, vec3 raydir)
{
	//SHADOWS

	vec4 MV_pos = gl_ModelViewMatrix*vec4(pos, 1.0);

	//vec3 shadowdir = normalize((MV_pos.xyz/MV_pos.w) - (MV_light.xyz/MV_light.w));
	vec3 shadowdir = -normalize(lightposition - pos);

	vec3 delta_shadow = shadowdir*0.002;
	//pos = pos-normalize(raydir)*stepsize;
	float shadowsample;
	for(int i = 0; i < 1400; i++)
	{
		shadowsample = texture3D(volumetexture,pos).x;
		if(shadowsample > Outsideisovalue+0.02)
		{

			return 1.0;
		}
		pos += delta_shadow;

		if (!(pos.x>0 && pos.x<1 && pos.y>0 && pos.y<1 && pos.z>0 && pos.z<1))
			break;

	}

	return 0.0;
	
}

(3)添加光照模型

vec3 PhongShading(vec3 pos, vec3 diffuseColor)
{
	vec3 shadedValue=vec3(0,0,0);
	
	
	vec3 G;
    G.x = (texture3D(volumetexture, pos+vec3(stepsize,0,0) ).x - texture3D(volumetexture, pos+vec3(-stepsize,0,0) ).x);
	G.y = (texture3D(volumetexture, pos+vec3(0,stepsize,0) ).y - texture3D(volumetexture, pos+vec3(0,-stepsize,0) ).y);
	G.z = (texture3D(volumetexture, pos+vec3(0,0,stepsize) ).z - texture3D(volumetexture, pos+vec3(0,0,-stepsize) ).z);	
	
	G = gl_NormalMatrix * normalize(G);
	G = normalize(G);
	
	vec4 MV_light = (gl_ModelViewMatrix*vec4(lightposition.xyz,1.0));
	
    vec3 L = normalize(MV_light.xyz/MV_light.w -eposition);	
    vec3 V = -normalize(eposition.xyz);
    
	//specularcolor
	vec3 H = normalize(V+L);
	float GdotH = pow(max(dot(G, H), 0.0), floor(shininess));
	float GdotL = max(dot(G, L), 0.0);
	
	vec3 specular = ks * vec3(1.0,1.0,1.0) * GdotH;
	vec3 diffuse = kd * diffuseColor.rgb * GdotL;
	vec3 ambient = ka * diffuseColor.rgb;
	
	shadedValue = specular + diffuse + ambient;
	
	return shadedValue;
}

 

实现效果如下:

 

 

实现过程中的几点说明:

 

OpenGL中,将模型显示在窗口中需要经过这样几个步骤:

(1)建立模型,得到模型顶点的坐标和法向量;对于复杂模型,还需要给出顶点顺序。

(2)建立世界坐标系,将模型放入到世界坐标系中,即将模型的顶点坐标和法向量等信息转为世界坐标系中的坐标。

(3)建立视图坐标系,即设定观察坐标系,通过确定视点,观察点和向上向量,三个向量确定视图坐标系。(lookat方法)

(4)设定透视方式,在视图坐标系的基础上选择裁剪方式,可以想象为相机调节焦距来控制显示的范围。

(5)设定画布的大小,可以设定最后输出的窗口大小。

 

这里主要是说明一下LookAt的使用,该方法有9个参数,分别是视点坐标、目标位置坐标和向上坐标方向。实际上就是确定了一个视图坐标系,也可以通过设定当前矩阵来替代LookAt方法。

这里前三列向量表示该坐标系的三个坐标轴,第四列实际上是原点的位置。下面给出我实现的方法:

 

  public double[] getCoordinate(double []vector){
        double []angle = new double[2];
        double len = Math.sqrt(vector[0]*vector[0] +vector[1]*vector[1] +vector[2] * vector[2]);
        if(Math.abs(len)<0.001) {
            len = 1;
        }
        for(int i = 0; i < 3; i ++){
            vector[i] = vector[i]/len;
        }
        angle[0] = Math.asin(vector[2]);
        angle[1] = Math.acos(vector[0]/Math.cos(angle[0]));
        return angle;
    }
    public void NormalVector(double[] vector) {
        double len = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1] + vector[2] * vector[2]);
        if (Math.abs(len) < 0.001) {
            len = 1;
        }
        for (int i = 0; i < 3; i++) {
            vector[i] = vector[i] / len;
        }
    }
    public double[] VectorCMulVector(double []vector1,double []vector2){
        double[] vector = {0,0,0};
        vector[0] = vector1[1] * vector2[2] - vector1[2] * vector2[1];
        vector[1] = vector1[2] * vector2[0] - vector1[0] *  vector2[2];
        vector[2] = vector1[0] * vector2[1] - vector1[1] *  vector2[0];
        return vector;
    }
    public  void gluLookAt(GL2 gl,double eye0 , double eye1, double eye2, double ref0 , double ref1 ,double ref2 , double up_dir0, double up_dir1, double up_dir2) {
        double [] directMat = new double[16];
        for(int i = 0; i < 16; i ++){
            directMat[i] = 0;
        }
         directMat[15]= 1;  
         
         double []fvDirect = {ref0-eye0,ref1-eye1,ref2-eye2};
         NormalVector(fvDirect);
         
         double []fvUpD = {up_dir0,up_dir1,up_dir2,};
         NormalVector(fvUpD);
         
         double []fvC = VectorCMulVector(fvDirect, fvUpD);
         NormalVector(fvC);
         
         double []fvUp = VectorCMulVector(fvC, fvDirect);
         NormalVector(fvUp);

         
        fvDirect[0] = -fvDirect[0];
        fvDirect[1] = -fvDirect[1];
        fvDirect[2] = -fvDirect[2];

        directMat[0] = fvC[0];
        directMat[4] = fvC[1];
        directMat[8] = fvC[2];
        directMat[1] = fvUp[0];
        directMat[5] = fvUp[1];
        directMat[9] = fvUp[2];
        directMat[2] = fvDirect[0];
        directMat[6] = fvDirect[1];
        directMat[10] = fvDirect[2];

        
        directMat[3] =  (float)eye0;
        directMat[7] =  (float)eye1;
        directMat[11] = (float)eye2;
        
        for(int i = 0; i < 16; i ++){
            modelView[i] = (float)directMat[i];
        }
    }

 

这里的modelView就是当前坐标矩阵了。

在漫游时LookAt方法比较有效,能够很方便的进行调节。在体绘制中,需要得到视图矩阵,根据视图矩阵来进行光线投射。通过上面的方法,可以直接获取到视图矩阵,完全不依赖于OpenGL,自己来实现。

通过这个方法,想明白OpenGL坐标系的关系,对OpenGL做深入的研究,可以说矩阵变换是基础,不可不深入了解。

具体参考了http://blog.csdn.net/liuzhidejll/article/details/7209640博客

 

在体绘制的过程中主要参考了下面一些资料:

[1]康玉之,GPU 编程与 CG语言之阳春白雪下里巴人

[2]Advanced Illumination Techniques for GPU-Based Volume Raycasting

[3]CUDA Volume Rendering  简介 

这些资料在我的资源里都可免费下载到,希望对体绘制的实现有一些帮助。

 

抱歉!评论已关闭.