上一次画球的方法是用基于角度切分做的,缺点是两极会收缩,赤道会被拉伸。这次的几何球将介绍使用正二十面体来画球,这样球就会被均匀的分布。其求解方法为,将二十面体的每一条边的外接圆弧等量划分,以生成球面的顶点。纹理坐标则是将二十面体展开再将其划分后计算纹理坐标。几何球也有一个缺点就是部分位置可以显示不到图像,因为正二十面体不能将整个图像占满。
我实现的效果如下
其实现较为复杂,需要的可以复制,渲染器如下
#ifndef REGULARBALLRENDER_H
#define REGULARBALLRENDER_H
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
#include <QOpenGLTexture>
#include <QOpenGLExtraFunctions>
#define PI 3.14159265f
class RegularBallRender
{
public:
RegularBallRender() = default;
void initsize(float scale,float aHalf,int n,QImage &img);
void initsize(float scale,float aHalf,int n,QColor &c);
void render(QOpenGLExtraFunctions *f,QMatrix4x4 &pMatrix,QMatrix4x4 &vMatrix,QMatrix4x4 &mMatrix,QVector3D &light,QVector3D &camera);
private:
QOpenGLShaderProgram program_;
QOpenGLBuffer vbo_;
QVector<GLfloat> verticesVec_,normalsVec_,textureVec_;
QOpenGLTexture *texture_{nullptr};
void devideBall(float r, //球的半径
QVector3D &start, //指向圆弧起点的向量
QVector3D &end, //指向圆弧终点的向量
int n, //圆弧分的份数
int i,
QVector3D &result3D); //求第i份在圆弧上的坐标(i为0和n时分别代表起点和终点坐标)
QVector<double> doolittle(double a[3][4], int length, int cols);
void readData(double a[3][4],int rowNum,int xnum,double AugMatrix[10][20]);
void prepareChoose(int times,int rowNum,double AugMatrix[10][20]);
void choose(int times,int rowNum,int xnum,double AugMatrix[10][20]);
void resolve(int times,int rowNum,int xnum,double AugMatrix[10][20]);
void findX(int rowNum,int xnum,double AugMatrix[10][20]);
void devideLine(QVector3D &start, //线段起点坐标
QVector3D &end, //线段终点坐标
int n, //线段分的份数
int i, QVector3D &result); //求第i份在线段上的坐标(i为0和n时分别代表起点和终点坐标)
};
#endif // REGULARBALLRENDER_H
#include <memory>
#include "regularballrender.h"
void RegularBallRender::initsize(float scale, float aHalf, int n, QImage &img)
{
program_.addCacheableShaderFromSourceFile(QOpenGLShader::Vertex,"vertex_tex_light.sh");
program_.addCacheableShaderFromSourceFile(QOpenGLShader::Fragment,"frag_tex_light.sh");
program_.link();
aHalf*=scale; //黄金长方形长边的一半
float bHalf=aHalf*0.618034f; //黄金长方形短边的一半
qreal r = ::sqrt(aHalf*aHalf + bHalf * bHalf);//几何球的半径
QVector<GLfloat> alVertix20;//正二十面体的顶点列表
QVector<int> alFaceIndex20;//用于卷绕构成正二十面体各个三角形的顶点编号列表
alVertix20 << 0 << aHalf << -bHalf;//对应图8-17的1号点
alVertix20 << 0 << aHalf << bHalf;//对应图8-17的2号点
alVertix20 << aHalf << bHalf << 0;//对应图8-17的3号点
alVertix20 << bHalf << 0 << -aHalf;//对应图8-17的4号点
alVertix20 << -bHalf << 0 << -aHalf;//对应图8-17的5号点
alVertix20 << -aHalf << bHalf << 0;//对应图8-17的6号点
alVertix20 << -bHalf << 0 << aHalf;//对应图8-17的7号点
alVertix20 << bHalf << 0 << aHalf;//对应图8-17的8号点
alVertix20 << aHalf << -bHalf << 0;//对应图8-17的9号点
alVertix20 << 0 <<-aHalf << -bHalf;//对应图8-17的10号点
alVertix20 << -aHalf <<-bHalf << 0;//对应图8-17的11号点
alVertix20 << 0 << -aHalf << bHalf;//对应图8-17的12号点
alFaceIndex20 << 0 << 1 << 2;
alFaceIndex20 << 0 << 2 << 3;
alFaceIndex20 << 0 << 3 << 4;
alFaceIndex20 << 0 << 4 << 5;
alFaceIndex20 << 0 << 5 << 1;
//第二行10个三角形的各个顶点的坐标编号
alFaceIndex20 << 1 << 6 << 7;
alFaceIndex20 << 1 << 7 << 2;
alFaceIndex20 << 2 << 7 << 8;
alFaceIndex20 << 2 << 8 << 3;
alFaceIndex20 << 3 << 8 << 9;
alFaceIndex20 << 3 << 9 << 4;
alFaceIndex20 << 4 << 9 << 10;
alFaceIndex20 << 4 << 10 << 5;
alFaceIndex20 << 5 << 10 << 6;
alFaceIndex20 << 5 << 6 << 1;
//第三行5个三角形的各个顶点的坐标编号
alFaceIndex20 << 6 << 11 << 7;
alFaceIndex20 << 7 << 11 << 8;
alFaceIndex20 << 8 << 11 << 9;
alFaceIndex20 << 9 << 11 << 10;
alFaceIndex20 << 10 << 11 << 6;
QVector<GLfloat> vertices20;
for(int i : alFaceIndex20){//对顶点编号列表进行循环
vertices20 << alVertix20.at(3 *i);//将当前编号顶点的X坐标值存入最终数组
vertices20 << alVertix20.at(3 *i +1);//将当前编号顶点的Y坐标值存入最终数组
vertices20 << alVertix20.at(3 *i + 2);//将当前编号顶点的Z坐标值存入最终数组
}
QVector<GLfloat> alVertix;
QVector<int> alFaceIndex;
int vnCount=0;//顶点计数器
for(int k=0;k<vertices20.count();k+=9)//对正二十面体中的每个三角形循环
{
QVector3D v1(vertices20[k+0], vertices20[k+1], vertices20[k+2]); //当前三角形3个
QVector3D v2(vertices20[k+3], vertices20[k+4], vertices20[k+5]);//顶点的坐标
QVector3D v3(vertices20[k+6], vertices20[k+7], vertices20[k+8]);
for(int i=0;i<=n;i++)
{//根据切分的份数求出几何球原始顶点的坐标
QVector3D viStart;
devideBall(r, v1, v2, n, i,viStart);//对圆弧进行切分
QVector3D viEnd;
devideBall(r, v1, v3, n, i,viEnd);//对圆弧进行切分
for(int j=0;j<=i;j++)
{
QVector3D vi;
devideBall(r, viStart, viEnd, i, j,vi);//对圆弧进行切分
alVertix << vi.x() << vi.y() << vi.z();//将坐标存入原始顶点列表
}
}
for(int i=0;i<n;i++)
{//循环生成构成几个球各个三角形的顶点编号列表
if(i==0){//若是第0行,顶点编号012
alFaceIndex << vnCount + 0 << vnCount + 1 << vnCount + 2;
vnCount+=1;//顶点计数器加1
if(i==n-1){ //如果是正二十面体三角形的最后一次循环,将下一拨的顶点个数也加上
vnCount+=2;
}
continue;
}
int iStart=vnCount;//第i行开始的编号(这里的行指的是平面展开图中的行)
int viCount=i+1;//第i行顶点数
int iEnd=iStart+viCount-1;//第i行结束顶点编号
int iStartNext=iStart+viCount;//第i+1行开始的顶点编号
int viCountNext=viCount+1;//第i+1行顶点数
int iEndNext=iStartNext+viCountNext-1;//第i+1行结束的顶点编号
for(int j=0;j<viCount-1;j++)
{//前面的四边形
int index0=iStart+j;//四边形4个顶点的编号
int index1=index0+1;
int index2=iStartNext+j;
int index3=index2+1;
//将四边形4个顶点卷绕成两个三角形
alFaceIndex << index0 << index2 << index3;
alFaceIndex << index0 << index3 << index1;
}
//最后一个三角形3个顶点的编号
alFaceIndex << iEnd << iEndNext - 1 << iEndNext;//最后一个三角形
vnCount+=viCount;//第i行前所有顶点数的和
if(i==n-1){ //如果是正二十面体三角形的最后一次循环,将下一拨的顶点个数也加上
vnCount+=viCountNext;
}
}
}
for(int i : alFaceIndex){
verticesVec_ << alVertix.at(i * 3 + 0) << alVertix.at(i * 3 + 1) << alVertix.at(i * 3 + 2);
}
normalsVec_ = verticesVec_;
QVector<GLfloat> alST20;
QVector<int> alTexIndex20;
float sSpan=1/5.5f;//每个纹理三角形的边长
float tSpan=1/3.0f;//每个纹理三角形的高
//下面4个循环生成了正二十面体展开后各个顶点的纹理坐标
for(int i=0;i<5;i++){
alST20 << sSpan+sSpan*i <<0;
}
for(int i=0;i<6;i++){
alST20 << sSpan/2+sSpan*i << tSpan;
}
for(int i=0;i<6;i++){
alST20 << sSpan*i << tSpan*2;
}
for(int i=0;i<5;i++){
alST20 << sSpan/2+sSpan*i << tSpan*3;
}
//第一行5个三角形的各个顶点的纹理坐标编号
alTexIndex20 << 0 << 5 << 6;
alTexIndex20 << 1 << 6 << 7;
alTexIndex20 << 2 << 7 << 8;
alTexIndex20 << 3 << 8 << 9;
alTexIndex20 << 4 << 9 << 10;
//第二行10个三角形的各个顶点的纹理坐标编号
alTexIndex20 << 5 << 11 << 12;
alTexIndex20 << 5 << 12 << 6;
alTexIndex20 << 6 << 12 << 13;
alTexIndex20 << 6 << 13 << 7;
alTexIndex20 << 7 << 13 << 14;
alTexIndex20 << 7 << 14 << 8;
alTexIndex20 << 8 << 14 << 15;
alTexIndex20 << 8 << 15 << 9;
alTexIndex20 << 9 << 15 << 16;
alTexIndex20 << 9 << 16 << 10;
//第三行5个三角形的各个顶点的纹理坐标编号
alTexIndex20 << 11 << 17 << 12;
alTexIndex20 << 12 << 18 << 13;
alTexIndex20 << 13 << 19 << 14;
alTexIndex20 << 14 << 20 << 15;
alTexIndex20 << 15 << 21 << 16;
QVector<GLfloat> st20;
for(int i : alTexIndex20){
st20 << alST20.at(i * 2 + 0) << alST20.at(i * 2 + 1);
}
QVector<GLfloat> alST; //几何球的原始纹理坐标列表(未卷绕)
for(int k=0;k<st20.count();k+=6)
{//对正二十面体的各个三角形进行循环
QVector3D st1(st20[k+0], st20[k+1], 0);//取出当前三角形
QVector3D st2(st20[k+2], st20[k+3], 0);//3个顶点的纹理坐标
QVector3D st3(st20[k+4], st20[k+5], 0);
for(int i=0;i<=n;i++)
{//对正二十面体平面展开图的边进行切分
QVector3D stiStart;
devideLine(st1, st2, n, i,stiStart);
QVector3D stiEnd;
devideLine(st1, st3, n, i,stiEnd);
for(int j=0;j<=i;j++)
{//计算几何球平面展开图顶点对应的纹理坐标
QVector3D sti;
devideLine(stiStart, stiEnd, i, j,sti);
alST << sti.x() << sti.y();//将纹理坐标加入列表
}
}
}
for(int i : alFaceIndex){
textureVec_ << alST.at(i * 2 + 0) << alST.at(i * 2 + 1);
}
texture_ = new QOpenGLTexture(img);
texture_->setWrapMode(QOpenGLTexture::ClampToEdge);
texture_->setMinMagFilters(QOpenGLTexture::NearestMipMapNearest,QOpenGLTexture::LinearMipMapNearest);
QVector<GLfloat> buffer;
buffer << verticesVec_ << normalsVec_ << textureVec_;
vbo_.create();
vbo_.bind();
vbo_.allocate(buffer.data(),buffer.count() * sizeof(GLfloat));
}
void RegularBallRender::initsize(float scale, float aHalf, int n, QColor &c)
{
program_.addCacheableShaderFromSourceFile(QOpenGLShader::Vertex,"vertex_color_light.sh");
program_.addCacheableShaderFromSourceFile(QOpenGLShader::Fragment,"frag_color_light.sh");
program_.link();
aHalf*=scale; //黄金长方形长边的一半
float bHalf=aHalf*0.618034f; //黄金长方形短边的一半
qreal r = ::sqrt(aHalf*aHalf + bHalf * bHalf);//几何球的半径
QVector<GLfloat> alVertix20;//正二十面体的顶点列表
QVector<int> alFaceIndex20;//用于卷绕构成正二十面体各个三角形的顶点编号列表
alVertix20 << 0 << aHalf << -bHalf;//对应图8-17的1号点
alVertix20 << 0 << aHalf << bHalf;//对应图8-17的2号点
alVertix20 << aHalf << bHalf << 0;//对应图8-17的3号点
alVertix20 << bHalf << 0 << -aHalf;//对应图8-17的4号点
alVertix20 << -bHalf << 0 << -aHalf;//对应图8-17的5号点
alVertix20 << -aHalf << bHalf << 0;//对应图8-17的6号点
alVertix20 << -bHalf << 0 << aHalf;//对应图8-17的7号点
alVertix20 << bHalf << 0 << aHalf;//对应图8-17的8号点
alVertix20 << aHalf << -bHalf << 0;//对应图8-17的9号点
alVertix20 << 0 <<-aHalf << -bHalf;//对应图8-17的10号点
alVertix20 << -aHalf <<-bHalf << 0;//对应图8-17的11号点
alVertix20 << 0 << -aHalf << bHalf;//对应图8-17的12号点
alFaceIndex20 << 0 << 1 << 2;
alFaceIndex20 << 0 << 2 << 3;
alFaceIndex20 << 0 << 3 << 4;
alFaceIndex20 << 0 << 4 << 5;
alFaceIndex20 << 0 << 5 << 1;
//第二行10个三角形的各个顶点的坐标编号
alFaceIndex20 << 1 << 6 << 7;
alFaceIndex20 << 1 << 7 << 2;
alFaceIndex20 << 2 << 7 << 8;
alFaceIndex20 << 2 << 8 << 3;
alFaceIndex20 << 3 << 8 << 9;
alFaceIndex20 << 3 << 9 << 4;
alFaceIndex20 << 4 << 9 << 10;
alFaceIndex20 << 4 << 10 << 5;
alFaceIndex20 << 5 << 10 << 6;
alFaceIndex20 << 5 << 6 << 1;
//第三行5个三角形的各个顶点的坐标编号
alFaceIndex20 << 6 << 11 << 7;
alFaceIndex20 << 7 << 11 << 8;
alFaceIndex20 << 8 << 11 << 9;
alFaceIndex20 << 9 << 11 << 10;
alFaceIndex20 << 10 << 11 << 6;
QVector<GLfloat> vertices20;
for(int i : alFaceIndex20){//对顶点编号列表进行循环
vertices20 << alVertix20.at(3 *i);//将当前编号顶点的X坐标值存入最终数组
vertices20 << alVertix20.at(3 *i +1);//将当前编号顶点的Y坐标值存入最终数组
vertices20 << alVertix20.at(3 *i + 2);//将当前编号顶点的Z坐标值存入最终数组
}
QVector<GLfloat> alVertix;
QVector<int> alFaceIndex;
int vnCount=0;//顶点计数器
for(int k=0;k<vertices20.count();k+=9)//对正二十面体中的每个三角形循环
{
QVector3D v1(vertices20[k+0], vertices20[k+1], vertices20[k+2]); //当前三角形3个
QVector3D v2(vertices20[k+3], vertices20[k+4], vertices20[k+5]);//顶点的坐标
QVector3D v3(vertices20[k+6], vertices20[k+7], vertices20[k+8]);
for(int i=0;i<=n;i++)
{//根据切分的份数求出几何球原始顶点的坐标
QVector3D viStart;
devideBall(r, v1, v2, n, i,viStart);//对圆弧进行切分
QVector3D viEnd;
devideBall(r, v1, v3, n, i,viEnd);//对圆弧进行切分
for(int j=0;j<=i;j++)
{
QVector3D vi;
devideBall(r, viStart, viEnd, i, j,vi);//对圆弧进行切分
alVertix << vi.x() << vi.y() << vi.z();//将坐标存入原始顶点列表
}
}
for(int i=0;i<n;i++)
{//循环生成构成几个球各个三角形的顶点编号列表
if(i==0){//若是第0行,顶点编号012
alFaceIndex << vnCount + 0 << vnCount + 1 << vnCount + 2;
vnCount+=1;//顶点计数器加1
if(i==n-1){ //如果是正二十面体三角形的最后一次循环,将下一拨的顶点个数也加上
vnCount+=2;
}
continue;
}
int iStart=vnCount;//第i行开始的编号(这里的行指的是平面展开图中的行)
int viCount=i+1;//第i行顶点数
int iEnd=iStart+viCount-1;//第i行结束顶点编号
int iStartNext=iStart+viCount;//第i+1行开始的顶点编号
int viCountNext=viCount+1;//第i+1行顶点数
int iEndNext=iStartNext+viCountNext-1;//第i+1行结束的顶点编号
for(int j=0;j<viCount-1;j++)
{//前面的四边形
int index0=iStart+j;//四边形4个顶点的编号
int index1=index0+1;
int index2=iStartNext+j;
int index3=index2+1;
//将四边形4个顶点卷绕成两个三角形
alFaceIndex << index0 << index2 << index3;
alFaceIndex << index0 << index3 << index1;
}
//最后一个三角形3个顶点的编号
alFaceIndex << iEnd << iEndNext - 1 << iEndNext;//最后一个三角形
vnCount+=viCount;//第i行前所有顶点数的和
if(i==n-1){ //如果是正二十面体三角形的最后一次循环,将下一拨的顶点个数也加上
vnCount+=viCountNext;
}
}
}
for(int i : alFaceIndex){
verticesVec_ << alVertix.at(i * 3 + 0) << alVertix.at(i * 3 + 1) << alVertix.at(i * 3 + 2);
textureVec_ << c.redF() << c.greenF() << c.blueF();
}
normalsVec_ = verticesVec_;
QVector<GLfloat> buffer;
buffer << verticesVec_ << normalsVec_ << textureVec_;
vbo_.create();
vbo_.bind();
vbo_.allocate(buffer.data(),buffer.count() * sizeof(GLfloat));
}
void RegularBallRender::render(QOpenGLExtraFunctions *f, QMatrix4x4 &pMatrix, QMatrix4x4 &vMatrix, QMatrix4x4 &mMatrix, QVector3D &light, QVector3D &camera)
{
f->glEnable(GL_DEPTH_TEST);
f->glEnable(GL_CULL_FACE);
program_.bind();
vbo_.bind();
if(texture_){
f->glActiveTexture(GL_TEXTURE0 + 0);
program_.setUniformValue("uPMatrix",pMatrix);
program_.setUniformValue("uVMatrix",vMatrix);
program_.setUniformValue("uMMatrix",mMatrix);
program_.setUniformValue("uLightLocation",light);
program_.setUniformValue("uCamera",camera);
program_.setUniformValue("sTexture",0);
program_.enableAttributeArray(0);
program_.enableAttributeArray(1);
program_.enableAttributeArray(2);
program_.setAttributeBuffer(0,GL_FLOAT,0,3,3*sizeof(GLfloat));
program_.setAttributeBuffer(1,GL_FLOAT,verticesVec_.count() * sizeof(GLfloat),3,3*sizeof(GLfloat));
program_.setAttributeBuffer(2,GL_FLOAT,(verticesVec_.count() + normalsVec_.count()) * sizeof(GLfloat),2,2*sizeof(GLfloat));
texture_->bind();
f->glDrawArrays(GL_TRIANGLES,0,verticesVec_.count() / 3);
program_.disableAttributeArray(0);
program_.disableAttributeArray(1);
program_.disableAttributeArray(2);
texture_->release();
vbo_.release();
program_.release();
}else{
program_.setUniformValue("uPMatrix",pMatrix);
program_.setUniformValue("uVMatrix",vMatrix);
program_.setUniformValue("uMMatrix",mMatrix);
program_.setUniformValue("uLightLocation",light);
program_.setUniformValue("uCamera",camera);
program_.enableAttributeArray(0);
program_.enableAttributeArray(1);
program_.enableAttributeArray(2);
program_.setAttributeBuffer(0,GL_FLOAT,0,3,3*sizeof(GLfloat));
program_.setAttributeBuffer(1,GL_FLOAT,verticesVec_.count() * sizeof(GLfloat),3,3*sizeof(GLfloat));
program_.setAttributeBuffer(2,GL_FLOAT,(verticesVec_.count() + normalsVec_.count()) * sizeof(GLfloat),3,3*sizeof(GLfloat));
f->glDrawArrays(GL_LINE_STRIP,0,verticesVec_.count() / 3);
program_.disableAttributeArray(0);
program_.disableAttributeArray(1);
program_.disableAttributeArray(2);
vbo_.release();
program_.release();
}
f->glDisable(GL_DEPTH_TEST);
f->glDisable(GL_CULL_FACE);
}
void RegularBallRender::devideBall(float r, QVector3D &start, QVector3D &end, int n, int i, QVector3D &result3D)
{
QVector3D s = start.normalized();
QVector3D e = end.normalized();
if(n == 0){
result3D.setX(s.x() * r);
result3D.setY(s.y() * r);
result3D.setZ(s.z() * r);
}
//求两个向量的夹角
double angrad=::acos(QVector3D::dotProduct(s,e)); //起点终点向量夹角
double angrad1=angrad*i/n; //所求向量和起点向量的夹角
double angrad2=angrad-angrad1; //所求向量和终点向量的夹角
QVector3D normal = QVector3D::crossProduct(s,e);
double matrix[3][4]={//用doolittle分解算法解n元一次线性方程组所需的系数矩阵
{s[0],s[1],s[2],::cos(angrad1)},
{e[0],e[1],e[2],::cos(angrad2)},
{normal[0],normal[1],normal[2],0}
};
QVector<double> result = doolittle(matrix,3,4);//解n元一次线性方程组
float x=(float) result.at(0);//得到从球心到所求点向量的规格化版本
float y=(float) result.at(1);
float z=(float) result.at(2);
result3D.setX(x);
result3D.setY(y);
result3D.setZ(z);
}
QVector<double> RegularBallRender::doolittle(double a[3][4], int length, int cols)
{
int rowNum = length;//获得未知数的个数
int xnum = cols-rowNum;// 所求解的组数(一)
double AugMatrix[10][20];//拓展的增广矩阵
readData(a,rowNum,xnum,AugMatrix);
for(int i=1;i<=rowNum;i++)
{
prepareChoose(i,rowNum,AugMatrix);
choose(i,rowNum,xnum,AugMatrix);
resolve(i,rowNum,xnum,AugMatrix);
}
findX(rowNum,xnum,AugMatrix);
QVector<double> result;
for(int i=0;i<rowNum;i++)
{
result << AugMatrix[i+1][rowNum+1];
}
return result;
}
void RegularBallRender::readData(double a[3][4], int rowNum, int xnum, double AugMatrix[10][20])
{//增广矩阵的拓展
for(int i=0;i<=rowNum;i++)
{
AugMatrix[i][0]=0;
}
for(int i=0;i<=rowNum+xnum;i++)
{
AugMatrix[0][i]=0;
}
for(int i=1;i<=rowNum;i++)
for(int j=1;j<=rowNum+xnum;j++)
AugMatrix[i][j]=a[i-1][j-1];
}
void RegularBallRender::prepareChoose(int times, int rowNum, double AugMatrix[10][20])
{//计算准备选主元
for(int i=times;i<=rowNum;i++)
{
for(int j=times-1;j>=1;j--)
{
AugMatrix[i][times]=AugMatrix[i][times]-AugMatrix[i][j]*AugMatrix[j][times];
}
}
}
void RegularBallRender::choose(int times, int rowNum, int xnum, double AugMatrix[10][20])
{//选主元
int line=times;
for(int i=times+1;i<=rowNum;i++)//选最大行
{
if(AugMatrix[i][times]*AugMatrix[i][times]>AugMatrix[line][times]*AugMatrix[line][times])
line=i;
}
if(AugMatrix[line][times]==0)//最大数等于零
{
qDebug() << "doolittle fail !!!";
}
if(line!=times)//交换
{
double temp;
for(int i=1;i<=rowNum+xnum;i++)
{
temp=AugMatrix[times][i];
AugMatrix[times][i]=AugMatrix[line][i];
AugMatrix[line][i]=temp;
}
}
}
void RegularBallRender::resolve(int times, int rowNum, int xnum, double AugMatrix[10][20])
{//分解
for(int i=times+1;i<=rowNum;i++)
{
AugMatrix[i][times]=AugMatrix[i][times]/AugMatrix[times][times];
}
for(int i=times+1;i<=rowNum+xnum;i++)
{
for(int j=times-1;j>=1;j--)
{
AugMatrix[times][i]=AugMatrix[times][i]-AugMatrix[times][j]*AugMatrix[j][i];
}
}
}
void RegularBallRender::findX(int rowNum, int xnum, double AugMatrix[10][20])
{
for(int k=1;k<=xnum;k++)
{//求解
AugMatrix[rowNum][rowNum+k]=AugMatrix[rowNum][rowNum+k]/AugMatrix[rowNum][rowNum];
for(int i=rowNum-1;i>=1;i--)
{
for(int j=rowNum;j>i;j--)
{
AugMatrix[i][rowNum+k]=AugMatrix[i][rowNum+k]-AugMatrix[i][j]*AugMatrix[j][rowNum+k];
}
AugMatrix[i][rowNum+k]=AugMatrix[i][rowNum+k]/AugMatrix[i][i];
}
}
}
void RegularBallRender::devideLine(QVector3D &start, QVector3D &end, int n, int i,QVector3D &result)
{
if(n==0){//如果n为零,返回起点坐标
return;
}
//求起点到终点的向量
QVector3D ab(end.x()-start.x(), end.y()-start.y(), end.z()-start.z());
float vecRatio=i/(float)n;//求向量比例
//求起点到所求点的向量
QVector3D ac(ab.x()*vecRatio, ab.y()*vecRatio, ab.z()*vecRatio);
//得到所求点坐标
float x=start.x()+ac.x();
float y=start.y()+ac.y();
float z=start.z()+ac.z();
//返回线段所求点坐标
result.setX(x);
result.setY(y);
result.setZ(z);
}
其图片纹理shader如下
#version 330
uniform mat4 uPMatrix,uVMatrix,uMMatrix;
uniform vec3 uLightLocation,uCamera;
layout (location = 0) in vec3 aPosition;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTextureCoord;
smooth out vec2 vTextureCood;
smooth out vec4 vAmbient;
smooth out vec4 vDiffuse;
smooth out vec4 vSpecular;
void pointLight(in vec3 normal,inout vec4 ambient,inout vec4 diffuse,inout vec4 specular,in vec4 lightAmbient,in vec4 lightDiffuse,in vec4 lightSpecular,in float shininess){
ambient = lightAmbient;
vec3 normalTarget = aPosition + normal;
vec3 newNormal = normalize((uMMatrix * vec4(normalTarget,1)).xyz - (uMMatrix * vec4(aPosition,1)).xyz);
vec3 eye = normalize(uCamera - (uMMatrix * vec4(aPosition,1)).xyz);
vec3 vp = normalize(uLightLocation - (uMMatrix * vec4(aPosition,1)).xyz);
vec3 halfVector = normalize(eye + vp);
float nDotViewPotision = max(0.0,dot(newNormal,vp));
diffuse = lightDiffuse * nDotViewPotision;
float nDotViewHalfVector = dot(newNormal,halfVector);
float powerFactor = max(0.0,pow(nDotViewHalfVector,shininess));
specular = lightSpecular * powerFactor;
}
void main(void)
{
gl_Position = uPMatrix * uVMatrix * uMMatrix * vec4(aPosition,1);
vec4 ambient = vec4(0.0,0.0,0.0,0.0),diffuse = vec4(0.0,0.0,0.0,0.0),specular = vec4(0.0,0.0,0.0,0.0);
pointLight(aNormal,ambient,diffuse,specular,vec4(0.6,0.6,0.6,1.0),vec4(0.8,0.8,0.8,1.0),vec4(0.9,0.9,0.9,1),50);
vAmbient = ambient;
vDiffuse = diffuse;
vSpecular = specular;
vTextureCood = aTextureCoord;
}
#version 330
uniform sampler2D sTexture;
in vec2 vTextureCood;
in vec4 vAmbient;
in vec4 vDiffuse;
in vec4 vSpecular;
out vec4 fragColor;
void main(void)
{
vec4 finalColor = texture2D(sTexture,vTextureCood);
finalColor = finalColor * (vAmbient + vDiffuse + vSpecular);
fragColor = finalColor;
}
其线条样式shader如下
#version 330
uniform mat4 uPMatrix,uVMatrix,uMMatrix;
uniform vec3 uLightLocation,uCamera;
layout (location = 0) in vec3 aPosition;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec3 aColor;
smooth out vec4 vColor;
smooth out vec4 vAmbient;
smooth out vec4 vDiffuse;
smooth out vec4 vSpecular;
void pointLight(in vec3 normal,inout vec4 ambient,inout vec4 diffuse,inout vec4 specular,in vec4 lightAmbient,in vec4 lightDiffuse,in vec4 lightSpecular,in float shininess){
ambient = lightAmbient;
vec3 normalTarget = aPosition + normal;
vec3 newNormal = normalize((uMMatrix * vec4(normalTarget,1)).xyz - (uMMatrix * vec4(aPosition,1)).xyz);
vec3 eye = normalize(uCamera - (uMMatrix * vec4(aPosition,1)).xyz);
vec3 vp = normalize(uLightLocation - (uMMatrix * vec4(aPosition,1)).xyz);
vec3 halfVector = normalize(eye + vp);
float nDotViewPotision = max(0.0,dot(newNormal,vp));
diffuse = lightDiffuse * nDotViewPotision;
float nDotViewHalfVector = dot(newNormal,halfVector);
float powerFactor = max(0.0,pow(nDotViewHalfVector,shininess));
specular = lightSpecular * powerFactor;
}
void main(void)
{
gl_Position = uPMatrix * uVMatrix * uMMatrix * vec4(aPosition,1);
vec4 ambient = vec4(0.0,0.0,0.0,0.0),diffuse = vec4(0.0,0.0,0.0,0.0),specular = vec4(0.0,0.0,0.0,0.0);
pointLight(aNormal,ambient,diffuse,specular,vec4(0.15,0.15,0.15,1.0),vec4(0.8,0.8,0.8,1.0),vec4(0.7,0.7,0.7,1),50);
vAmbient = ambient;
vDiffuse = diffuse;
vSpecular = specular;
vColor = vec4(aColor,1);
}
#version 330
uniform sampler2D sTexture;
in vec4 vColor;
in vec4 vAmbient;
in vec4 vDiffuse;
in vec4 vSpecular;
out vec4 fragColor;
void main(void)
{
vec4 finalColor = vColor * (vAmbient + vDiffuse + vSpecular);
fragColor = finalColor;
}
其使用方式和前面一样
#ifndef WIDGET_H
#define WIDGET_H
#include <QOpenGLWidget>
#include <QTimer>
#include "regularballrender.h"
class Widget : public QOpenGLWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
protected:
void initializeGL() override;
void resizeGL(int w,int h) override;
void paintGL() override;
private:
QTimer tm_;
RegularBallRender render_;
QMatrix4x4 pMatrix_;
QVector3D camera_,light_;
qreal angleX_= 0, angleY_ = 0, angleZ_ = 0;
private slots:
void slotTimeout();
};
#endif // WIDGET_H
#include "widget.h"
Widget::Widget(QWidget *parent)
: QOpenGLWidget(parent)
{
connect(&tm_,SIGNAL(timeout()),this,SLOT(slotTimeout()));
tm_.start(45);
}
Widget::~Widget()
{
}
void Widget::initializeGL()
{
render_.initsize(2.0,1.6,5,QImage("test.jpg"));
// render_.initsize(2.0,1.6,5,QColor(Qt::white));
light_.setX(20);
light_.setY(3);
light_.setZ(0);
camera_.setX(0);
camera_.setY(0);
camera_.setZ(3);
}
void Widget::resizeGL(int w, int h)
{
pMatrix_.setToIdentity();
pMatrix_.perspective(45,float(w)/h,0.01f,100.0f);
}
void Widget::paintGL()
{
QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions();
f->glClearColor(0.0, 0.0, 0.0, 0.0);
f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
QMatrix4x4 vMatrix;
vMatrix.lookAt(camera_,QVector3D(0,0,0),QVector3D(0,1,0));
QMatrix4x4 mMatrix;
mMatrix.rotate(angleX_,1,0,0);
mMatrix.rotate(angleY_,0,1,0);
mMatrix.rotate(angleZ_,0,0,1);
render_.render(f,pMatrix_,vMatrix,mMatrix,light_,camera_);
}
void Widget::slotTimeout()
{
angleX_ += 5;
angleY_ += 5;
angleZ_ += 5;
update();
}
到此结束,谢谢