zoukankan      html  css  js  c++  java
  • 从头開始绘制一个球体

    好久都没有更新博客了,近期在研究OpenGL图形编程,写了一些有趣的程序,分享一下. 废话少说,開始吧.

    球体作为主要的几何图形在游戏程序中应用广泛,当中最为人所知的是能够作为Sky Dome模拟天空,比起Sky Box来说更加仔细一些,即使加上别的特殊效果也不easy穿帮.

    我们在中学的数学课上应该学过球体的參数方程

    x = r * sin(angZ) * cos(angXY) y = r * sin(angZ) * sin(angXY) z = r * cos(angZ)

    angZ是纵向夹角,angXY是横向夹角,r是半径. 想起来了吧,用这个方程能够表示球体表面上的点,对了用这个方程就可画出球体模型.

    先让看一下须要定义哪些变量与函数:

    #ifndef PI
    #define PI 3.1415926//这个不用解释了
    #endif
    #ifndef PI2
    #define PI2 6.2831853//2PI
    #endif
    
    class Sphere {
    private:
    	GLuint* vboId;
    	GLuint vert,texcoord;
    	GLfloat* verts;//保存顶点与法向量的指针
    	GLfloat* texcoords;//保存纹理坐标的指针
    	int vertNum;
    public:
    	Sphere(int m,int n);//m是纵向细分程度,n是横向细分程度
    	~Sphere();
    	void render();//渲染球体!
    };

    当中构造函数里面的m与n分别表示纵向与横向的细分程度,值越大则球体看上去越精细. GLuint,GLfloat是OpenGL的变量类型,相当于C++中的unsigned int和float.


    接着来看一下构造球体最基本的部分:

    	vertNum=m*n*4;//顶点总数
    	verts=new GLfloat[vertNum*3];//每一个顶点有xyz三个分量,因此*3
    	texcoords=new GLfloat[vertNum*2];//每一个顶点的纹理坐标有uv两个分量,因此*2
    
    	float stepAngZ=PI/m;//纵向角度每次添加的值
    	float stepAngXY=PI2/n;//横向角度每次添加的值
    	float angZ=0.0;//初始的纵向角度
    	float angXY=0.0;//初始的横向角度
    
    	int index=0;
    	int indexTex=0;
    	for(int i=0;i<m;i++) {
    		for(int j=0;j<n;j++) {
                            //构造一个顶点
                            float x1=sin(angZ)*cos(angXY);
    		        float y1=sin(angZ)*sin(angXY);
    			float z1=cos(angZ);
    			verts[index]=x1; index++;
    			verts[index]=y1; index++;
    			verts[index]=z1; index++;
    			float v1=angZ/PI;
    			float u1=angXY/PI2;
    			texcoords[indexTex]=u1; indexTex++;
    			texcoords[indexTex]=v1; indexTex++;
    
    			float x2=sin(angZ+stepAngZ)*cos(angXY);
    			float y2=sin(angZ+stepAngZ)*sin(angXY);
    			float z2=cos(angZ+stepAngZ);
    			verts[index]=x2; index++;
    			verts[index]=y2; index++;
    			verts[index]=z2; index++;
    			float v2=(angZ+stepAngZ)/PI;
    			float u2=angXY/PI2;
    			texcoords[indexTex]=u2; indexTex++;
    			texcoords[indexTex]=v2; indexTex++;
    
    
    			float x3=sin(angZ+stepAngZ)*cos(angXY+stepAngXY);
    			float y3=sin(angZ+stepAngZ)*sin(angXY+stepAngXY);
    			float z3=cos(angZ+stepAngZ);
    			verts[index]=x3; index++;
    			verts[index]=y3; index++;
    			verts[index]=z3; index++;
    			float v3=(angZ+stepAngZ)/PI;
    			float u3=(angXY+stepAngXY)/PI2;
    			texcoords[indexTex]=u3; indexTex++;
    			texcoords[indexTex]=v3; indexTex++;
    
    			float x4=sin(angZ)*cos(angXY+stepAngXY);
    			float y4=sin(angZ)*sin(angXY+stepAngXY);
    			float z4=cos(angZ);
    			verts[index]=x4; index++;
    			verts[index]=y4; index++;
    			verts[index]=z4; index++;
    			float v4=angZ/PI;
    			float u4=(angXY+stepAngXY)/PI2;
    			texcoords[indexTex]=u4; indexTex++;
    			texcoords[indexTex]=v4; indexTex++;
    
    			angXY+=stepAngXY;
    		}
    		angXY=0.0;//每次横向到达2PI角度则横向角度归0
    		angZ+=stepAngZ;
    	}


    通过添加固定角度来构造球体网格,至于为什么球体的法向量与球体表面的顶点坐标是一致的,请看下面手绘:


    明确了吧,p点上的法向量就是从原点到p点的向量,简单吧.
    接着是纹理坐标的计算,既然横向夹角的范围是[0,2PI],纵向夹角的坐标是[0,PI],纹理坐标的范围是[0,1],那么把每一个顶点的横向与纵向的夹角相应的值按比例变到[0,1]就行计算出每一个顶点的纹理坐标了. 
    嗯, 既然顶点,法向量,纹理坐标都有了,那么把这些数据都提交到显存中去,然后渲染出图像吧.



    嗯,结果就是这样. 圆吧?

    以上结果使用OpenGL渲染,使用DirectX也可以实现同样的效果.

  • 相关阅读:
    oculus按键大全
    10 soundJs 初体验
    09 获取服务器时间
    08 基本数据类型转换
    07 如果再使用animateCC2018或者苹果系统使用animate时出现Uncaught ReferenceError: lib is not defined的错误
    AS3.0和php数据交互POST方式
    06 显示fps帧频
    05 js利用ajax向服务器发送请求并返回json值
    04 ajax执行php并传递参数
    03php拉取服务器信息并生成json
  • 原文地址:https://www.cnblogs.com/yutingliuyl/p/6739591.html
Copyright © 2011-2022 走看看