qt opengl 3d基本形状-圆柱

      圆柱的顶面和底面都是一个圆形,其顶点坐标为(R*cos(弧度),y,R*sin(弧度))其中R分别为圆柱高的峰值,比如正放于中心时y就是h/2或者-h/2,h/2表示顶面,-h/2表示底面。其纹理坐标为(0.5-0.5*cos(弧度),0.5-0.5*sin(弧度)),法向量为垂直向上或向下的单位向量。

     圆柱的侧面可以将其分割为n份,用GL_TRIANGLE_FAN绘制三角形的方式将其绘出;我们可以以弧度为分割标准,将其分成n份,其中分成的小三角形为两个相邻弧度求出的上、下顶点的四个坐标;纹理坐标可理解为弧度占整个圆周的比例,以此来从0-1中取值得到纹理坐标;法向量则为其顶点坐标。

    了解了圆柱的顶点生成原理后,我们就可以写出渲染器了,其实现如下

#ifndef CYLINDERRENDER_H
#define CYLINDERRENDER_H

#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
#include <QOpenGLTexture>
#include <QOpenGLExtraFunctions>
#define PI 3.14159265f
class CylinderRender
{
public:
    CylinderRender() = default;
    void initsize(QImage &ce,QImage &top,QImage &bottom,float r,float h);
    void render(QOpenGLExtraFunctions *f,QMatrix4x4 &pMatrix,QMatrix4x4 &vMatrix,QMatrix4x4 &mMatrix,QVector3D &light,QVector3D &camera);

private:
    QOpenGLShaderProgram program_;
    QOpenGLBuffer vbo_;
    QVector<GLfloat> vertVec_,textVec_,normalVec_;
    QVector<GLfloat> ceVec,ceTextVec,ceNorVec,topVec,topTexVec,topNorVec,bottomVec,bottomTexVec,bottomNorVec;
    QOpenGLTexture *ceTexture_{nullptr},*topTexture_{nullptr},*bottomTexture_{nullptr};
};

#endif // CYLINDERRENDER_H
#include "cylinderrender.h"

void CylinderRender::initsize(QImage &ce, QImage &top, QImage &bottom, float r, float h)
{
    program_.addCacheableShaderFromSourceFile(QOpenGLShader::Vertex,"vsrc.vert");
    program_.addCacheableShaderFromSourceFile(QOpenGLShader::Fragment,"fsrc.frag");
    program_.link();

    topTexture_ = new QOpenGLTexture(top);
    bottomTexture_ = new QOpenGLTexture(bottom);
    ceTexture_ = new QOpenGLTexture(ce);
    topTexture_->setWrapMode(QOpenGLTexture::ClampToEdge);
    topTexture_->setMinMagFilters(QOpenGLTexture::NearestMipMapNearest,QOpenGLTexture::LinearMipMapNearest);
    bottomTexture_->setWrapMode(QOpenGLTexture::ClampToEdge);
    bottomTexture_->setMinMagFilters(QOpenGLTexture::NearestMipMapNearest,QOpenGLTexture::LinearMipMapNearest);
    ceTexture_->setWrapMode(QOpenGLTexture::ClampToEdge);
    ceTexture_->setMinMagFilters(QOpenGLTexture::NearestMipMapNearest,QOpenGLTexture::LinearMipMapNearest);
    float angleSpan = 5;
    topVec << 0 << h/2 << 0;
    topTexVec << 0.5 << 0.5;
    topNorVec << 0 << 1 << 0;
    bottomVec << 0 << -h/2 << 0;
    bottomTexVec << 0.5 << 0.5;
    bottomNorVec << 0 << -1 << 0;
    for(float angle = 0; angle <= 360; angle += angleSpan){
        //侧面
        float curRad = angle * PI / 180;
        float x1 = r * ::cos(curRad);
        float y1 = - h/2;
        float z1 = r * ::sin(curRad);
        float hx1 = (angle/360);
        float hy1 = 0;
        float nx1 = x1;
        float ny1 = 0;
        float nz1 = z1;

        float x2 = x1;
        float y2 = h/2;
        float z2 = z1;
        float hx2 = hx1;
        float hy2 = 1;
        float nx2 = x2;
        float ny2 = 0;
        float nz2 = z2;

        float nextRad = angle + angleSpan;
        float x3 = r * ::cos(nextRad * PI / 180);
        float y3 = h/2;
        float z3 = r * ::sin(nextRad * PI / 180);
        float hx3 = (nextRad/360);
        float hy3 = 1;
        float nx3 = x3;
        float ny3 = 0;
        float nz3 = z3;

        float x4 = x3;
        float y4 = -h/2;
        float z4 = z3;
        float hx4 = hx3;
        float hy4 = 0;
        float nx4 = x4;
        float ny4 = 0;
        float nz4 = z4;
        ceVec << x1 << y1 << z1 << x2 << y2 << z2 << x3 << y3 << z3 << x4 << y4 << z4;
        ceTextVec << hx2 << hy2  << hx1 << hy1<< hx4 << hy4 << hx3 << hy3;
        ceNorVec << nx1 << ny1 << nz1 << nx2 << ny2 << nz2 << nx3 << ny3 << nz3 << nx4 << ny4 << nz4;
        //顶面
        x2 = r * ::cos(-curRad);
        z2 = r * ::sin(-curRad);
        topVec << x2 << y2 << z2;
        float topTx1 = 0.5 - 0.5 * ::cos(curRad);
        float topTy1 = 0.5 - 0.5 * ::sin(-curRad);
        topTexVec << topTx1 << topTy1;
        topNorVec << 0 << 1 << 0;
        //底面
        bottomVec << x1 << y1 << z1;
        bottomTexVec << topTx1 << topTy1;
        bottomNorVec << 0 << -1 << 0;
    }
    vertVec_ << ceVec  << topVec << bottomVec;
    textVec_ << ceTextVec << topTexVec << bottomTexVec;
    normalVec_ << ceNorVec << topNorVec << bottomNorVec;

    QVector<GLfloat> bytesVec;
    bytesVec << vertVec_ << textVec_ << normalVec_;
    vbo_.create();
    vbo_.bind();
    vbo_.allocate(bytesVec.data(),bytesVec.count() * sizeof GLfloat);
}

void CylinderRender::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();
    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("sTextures",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,vertVec_.count() * sizeof(GLfloat),2,2*sizeof(GLfloat));
    program_.setAttributeBuffer(2,GL_FLOAT,(vertVec_.count() + textVec_.count())*sizeof(GLfloat),3,3*sizeof(GLfloat));
    ceTexture_->bind();
    f->glDrawArrays(GL_QUADS,0,ceVec.count()/3);
    topTexture_->bind();
    f->glDrawArrays(GL_TRIANGLE_FAN,ceVec.count()/3,topVec.count()/3);
    bottomTexture_->bind();
    f->glDrawArrays(GL_TRIANGLE_FAN,(ceVec.count() + topVec.count())/3,bottomVec.count()/3);

    program_.disableAttributeArray(0);
    program_.disableAttributeArray(1);
    program_.disableAttributeArray(2);
    topTexture_->release();
    ceTexture_->release();
    bottomTexture_->release();
    vbo_.release();
    program_.release();

    f->glDisable(GL_CULL_FACE);
    f->glDisable(GL_DEPTH_TEST);
}

其shader和平时的差不多,只不过用了上、下、侧三个纹理

#version 330
uniform mat4 uPMatrix,uVMatrix,uMMatrix;
uniform vec3 uCamera,uLightLocation;
layout (location = 0)in vec3 aPosition;
layout (location = 1)in vec2 aTexture;
layout (location = 2)in vec3 aNormal;
smooth out vec3 vPosition;
smooth out vec2 vTexture;
smooth out vec4 vAmbient,vDiffuse,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.5,0.5,0.5,1),vec4(0.8,0.8,0.8,1),vec4(0.7,0.7,0.7,1),50.0);
    vPosition = aPosition;
    vTexture = aTexture;
    vAmbient = ambient;
    vDiffuse = diffuse;
    vSpecular = specular;
}
#version 330
uniform sampler2D sTextures;
in vec3 vPosition;
in vec4 vAmbient,vDiffuse,vSpecular;
in vec2 vTexture;
out vec4 fragColor;

void main(void)
{
    vec4 color = vec4(0.0,0.0,0.0,1.0);
    color = texture2D(sTextures,vTexture);
    fragColor = color * (vAmbient + vDiffuse + vSpecular);
}

其使用和前面一样了,传入参数即可

#ifndef WIDGET_H
#define WIDGET_H

#include <QOpenGLWidget>
#include <QTimer>
#include "cylinderrender.h"
class Widget : public QOpenGLWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();

protected:
    void paintGL() override;
    void resizeGL(int w,int h) override;
    void initializeGL() override;
    void mouseMoveEvent(QMouseEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;

private:
    QMatrix4x4 pMatrix_;
    QVector3D lightLocation_,camera_;
    CylinderRender render_;
    QTimer tm;
    qreal angleX_ = 0,angleY_ = 0,angleZ_ = 0;

private slots:
    void slotTimeout();
};

#endif // WIDGET_H
#include <QTimer>
#include "widget.h"

Widget::Widget(QWidget *parent)
    : QOpenGLWidget(parent)
{
    connect(&tm,SIGNAL(timeout()),this,SLOT(slotTimeout()));
    tm.start(40);
}

Widget::~Widget()
{

}

void Widget::paintGL()
{
    QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions();
    f->glClearColor(0.0,0.0,0.0,1.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,lightLocation_,camera_);
}

void Widget::resizeGL(int w, int h)
{
    pMatrix_.setToIdentity();
    pMatrix_.perspective(45,float(w)/h,0.01f,100.0f);
}

void Widget::initializeGL()
{
    render_.initsize(QImage("rect.jpg"),QImage("circle.jpg"),QImage("circle.jpg"),0.8,0.8);
    camera_.setX(0);
    camera_.setY(0);
    camera_.setZ(3);
    lightLocation_.setX(5);
    lightLocation_.setY(2);
    lightLocation_.setZ(1);
}

void Widget::mouseMoveEvent(QMouseEvent *event)
{
    Q_UNUSED(event)
    angleX_ += 5;
    angleY_ += 5;
    angleZ_ += 5;
    update();
}

void Widget::mousePressEvent(QMouseEvent *event)
{
    tm.stop();
}

void Widget::mouseReleaseEvent(QMouseEvent *event)
{
    tm.start();
}

void Widget::slotTimeout()
{
    angleX_ += 5;
    angleY_ += 5;
    angleZ_ += 5;
    update();
}

 到此结束。

猜你喜欢

转载自blog.csdn.net/wanghualin033/article/details/83451105