qt opengl 几何球

    上一次画球的方法是用基于角度切分做的,缺点是两极会收缩,赤道会被拉伸。这次的几何球将介绍使用正二十面体来画球,这样球就会被均匀的分布。其求解方法为,将二十面体的每一条边的外接圆弧等量划分,以生成球面的顶点。纹理坐标则是将二十面体展开再将其划分后计算纹理坐标。几何球也有一个缺点就是部分位置可以显示不到图像,因为正二十面体不能将整个图像占满。

我实现的效果如下

 

其实现较为复杂,需要的可以复制,渲染器如下

 

#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();
}

到此结束,谢谢

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值