qt opengl 3d基本形状圆锥

   圆锥与上一节的圆柱很相似,其侧面的顶点坐标同样拆分成很多个小三角形,顶点位于一点,底面顶点和圆柱计算底面顶点的方法一样,都是(r*cos(弧度),-h/2,r*sin(弧度));底面顶点纹理坐标的计算是一样的,而顶点的纹理坐标就是(0.5,1)了;其法向量计算有所不同;

      顶点的法向量为各个棱所在法向量的平均值,由于圆锥为对称图形,可知法向量为(0,1,0);

      底面的法向量为(0,-1,0);

      侧面圆周上的顶点法向量计算方法如下

                设顶点与底面中心的向量为AB,底面中心与圆周上某点的向量为AC,顶点与底面圆周上某点的向量为BC。

               1、求出ABC的面法向量: AB与AC的叉积

               2、用求出的面法向量与BC向量做叉积就能得到BC棱上的法向量,也即是C点的法向量

其效果如下

    其渲染器实现如下

#ifndef YUANZHUIRENDER_H
#define YUANZHUIRENDER_H

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

private:
    QOpenGLShaderProgram program_;
    QOpenGLBuffer vbo_;
    QVector<GLfloat> ceVert_,ceText_,ceNor_,bottomVert_,bottomText_,bottomNor_;
    QOpenGLTexture *textureCe_{nullptr},*textureBottom_{nullptr};
};

#endif // YUANZHUIRENDER_H
#include "yuanzhuirender.h"

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

    textureBottom_ = new QOpenGLTexture(bottom);
    textureCe_ = new QOpenGLTexture(ce);
    textureBottom_->setWrapMode(QOpenGLTexture::ClampToEdge);
    textureBottom_->setMinMagFilters(QOpenGLTexture::NearestMipMapNearest,QOpenGLTexture::LinearMipMapNearest);
    textureCe_->setWrapMode(QOpenGLTexture::ClampToEdge);
    textureCe_->setMinMagFilters(QOpenGLTexture::NearestMipMapNearest,QOpenGLTexture::LinearMipMapNearest);
    qreal angleSpan = 5;
    ceVert_ << 0 << h / 2 << 0;
    ceText_ << 0.5 << 1.0;
    ceNor_ << 0 << 1 << 0;
    bottomVert_ << 0 << -h/2 << 0;
    bottomText_ << 0.5 << 0.5;
    bottomNor_ << 0 << -1 << 0;
    for(qreal 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 tx1 = curRad / (2*PI);
        float ty1 = 0;

        ceVert_ << x1 << y1 << z1 ;
        ceText_ << tx1 << ty1;
        QVector3D norPlane = QVector3D::normal(QVector3D(0,h/2,0),QVector3D(0,-h/2,0),QVector3D(x1,y1,z1));
        QVector3D norP = QVector3D::crossProduct(norPlane,QVector3D(x1 - 0,y1 - h / 2,z1 -0));
        ceNor_ << norP.x() << norP.y() << norP.z();

        float bottomTx1 = 0.5 - 0.5 * ::cos(curRad);
        float bottomTy1 = 0.5 - 0.5 * ::sin(curRad);
        bottomVert_ << x1 << y1 << z1;
        bottomText_ << bottomTx1 << bottomTy1;
        bottomNor_ << 0 << -1 << 0;
    }
    QVector<GLfloat> vertVec;
    vertVec << ceVert_ << bottomVert_ << ceText_ << bottomText_ << ceNor_ << bottomNor_;
    vbo_.create();
    vbo_.bind();
    vbo_.allocate(vertVec.data(),vertVec.count() * sizeof(GLfloat));
}

void YuanZhuiRender::render(QOpenGLExtraFunctions *f, QMatrix4x4 &pMatrix, QMatrix4x4 &vMatrix, QMatrix4x4 &mMatrix, QVector3D &light, QVector3D &camera)
{
    f->glEnable(GL_DEPTH_TEST);

    program_.bind();
    vbo_.bind();
    f->glActiveTexture(GL_TEXTURE0);
    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,(ceVert_.count() + bottomVert_.count()) * sizeof(GLfloat),2,2*sizeof(GLfloat));
    program_.setAttributeBuffer(2,GL_FLOAT,(ceVert_.count() + bottomVert_.count() + ceText_.count() + bottomText_.count())*sizeof(GLfloat),3,3*sizeof(GLfloat));
    textureCe_->bind();
    f->glDrawArrays(GL_TRIANGLE_FAN,0,ceVert_.count() / 3);
    textureBottom_->bind();
    f->glDrawArrays(GL_TRIANGLE_FAN,ceVert_.count() / 3,bottomVert_.count() / 3);
    program_.disableAttributeArray(0);
    program_.disableAttributeArray(1);
    program_.disableAttributeArray(2);
    textureCe_->release();
    textureBottom_->release();
    vbo_.release();
    program_.release();

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

可以看到其shader与上一节使用的圆柱的shader是一样的,只是生成的顶点、纹理、法向量不同而已。

其使用如下

#ifndef WIDGET_H
#define WIDGET_H

#include <QOpenGLWidget>
#include <QTimer>
#include "yuanzhuirender.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;
    void mousePressEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;

private:
    YuanZhuiRender render_;
    QTimer tm;
    QMatrix4x4 pMatrix;
    QVector3D light_,camera_;
    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(30);
}

Widget::~Widget()
{

}

void Widget::initializeGL()
{
    render_.initsize(0.8,0.5,QImage("bottom.jpg"),QImage("rect.jpg"));
    light_.setX(10);
    light_.setY(6);
    light_.setZ(0);
    camera_.setX(0);
    camera_.setY(0);
    camera_.setZ(2);
}

void Widget::resizeGL(int w, int h)
{
    pMatrix.setToIdentity();
    pMatrix.perspective(45.0,float(w)/h,0.01f,200.0f);
}

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.0,0.0),QVector3D(0.0,1.0,0.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::mousePressEvent(QMouseEvent *event)
{
    Q_UNUSED(event)
    tm.stop();
}

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

void Widget::slotTimeout()
{
    angleX += 5;
    angleY += 5;
    angleZ += 5;
    update();
}

到此结束。

猜你喜欢

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