From ac16dfd325bf48f80161422b32e9470ea0346f19 Mon Sep 17 00:00:00 2001 From: Matt Williams Date: Wed, 19 Mar 2014 21:26:04 +0000 Subject: [PATCH] Update the BasicExample to use OpenGL 3 The example now uses OpenGL 3 features like Vertex Array Objects and uses no immediate mode stuff. Qt5 is used for some features like matrices and shaders. There is now no dependency on GLEW either. --- examples/Basic/CMakeLists.txt | 8 +- examples/Basic/OpenGLWidget.cpp | 231 ++++++++++++++++++++------------ examples/Basic/OpenGLWidget.h | 15 ++- examples/Basic/main.cpp | 6 +- 4 files changed, 158 insertions(+), 102 deletions(-) diff --git a/examples/Basic/CMakeLists.txt b/examples/Basic/CMakeLists.txt index e0328805..66515549 100644 --- a/examples/Basic/CMakeLists.txt +++ b/examples/Basic/CMakeLists.txt @@ -32,17 +32,13 @@ SET(INC_FILES OpenGLWidget.h ) -add_definitions(-DGLEW_STATIC) - #"Sources" and "Headers" are the group names in Visual Studio. #They may have other uses too... SOURCE_GROUP("Sources" FILES ${SRC_FILES}) SOURCE_GROUP("Headers" FILES ${INC_FILES}) -FIND_PACKAGE(OpenGL REQUIRED) - #Tell CMake the paths for OpenGL and for PolyVox (which is just relative to our current location) -INCLUDE_DIRECTORIES(${OPENGL_INCLUDE_DIR} ${PolyVoxCore_BINARY_DIR}/include ${PolyVoxCore_SOURCE_DIR}/include ${GLEW_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${PolyVoxCore_BINARY_DIR}/include ${PolyVoxCore_SOURCE_DIR}/include) LINK_DIRECTORIES(${PolyVoxCore_BINARY_DIR}) #Build @@ -50,7 +46,7 @@ ADD_EXECUTABLE(BasicExample ${SRC_FILES}) IF(MSVC) SET_TARGET_PROPERTIES(BasicExample PROPERTIES COMPILE_FLAGS "/W4 /wd4127") ENDIF(MSVC) -TARGET_LINK_LIBRARIES(BasicExample glew Qt5::OpenGL ${OPENGL_gl_LIBRARY} ${OPENGL_glu_LIBRARY} PolyVoxCore) +TARGET_LINK_LIBRARIES(BasicExample Qt5::OpenGL PolyVoxCore) SET_PROPERTY(TARGET BasicExample PROPERTY FOLDER "Examples") #Install - Only install the example in Windows diff --git a/examples/Basic/OpenGLWidget.cpp b/examples/Basic/OpenGLWidget.cpp index f33a8c7c..37f972af 100644 --- a/examples/Basic/OpenGLWidget.cpp +++ b/examples/Basic/OpenGLWidget.cpp @@ -1,6 +1,8 @@ #include "OpenGLWidget.h" #include +#include +#include using namespace PolyVox; using namespace std; @@ -9,126 +11,179 @@ OpenGLWidget::OpenGLWidget(QWidget *parent) :QGLWidget(parent) ,m_xRotation(0) ,m_yRotation(0) + ,gl(nullptr) { } -void OpenGLWidget::setSurfaceMeshToRender(const PolyVox::SurfaceMesh& surfaceMesh) +void OpenGLWidget::setSurfaceMeshToRender(const PolyVox::SurfaceMesh& surfaceMesh) { //Convienient access to the vertices and indices - const vector& vecIndices = surfaceMesh.getIndices(); - const vector& vecVertices = surfaceMesh.getVertices(); - - //Build an OpenGL index buffer - glGenBuffers(1, &indexBuffer); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); - const GLvoid* pIndices = static_cast(&(vecIndices[0])); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, vecIndices.size() * sizeof(uint32_t), pIndices, GL_STATIC_DRAW); - - //Build an OpenGL vertex buffer - glGenBuffers(1, &vertexBuffer); - glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); - const GLvoid* pVertices = static_cast(&(vecVertices[0])); - glBufferData(GL_ARRAY_BUFFER, vecVertices.size() * sizeof(PositionMaterialNormal), pVertices, GL_STATIC_DRAW); - - m_uBeginIndex = 0; - m_uEndIndex = vecIndices.size(); + const auto& vecIndices = surfaceMesh.getIndices(); + const auto& vecVertices = surfaceMesh.getVertices(); + + //Create the VAO for the mesh + gl->glGenVertexArrays(1, &vertexArrayObject); + gl->glBindVertexArray(vertexArrayObject); + + //The GL_ARRAY_BUFFER will contain the list of vertex positions + gl->glGenBuffers(1, &vertexBuffer); + gl->glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); + gl->glBufferData(GL_ARRAY_BUFFER, vecVertices.size() * sizeof(PositionMaterial), vecVertices.data(), GL_STATIC_DRAW); + + //and GL_ELEMENT_ARRAY_BUFFER will contain the indices + gl->glGenBuffers(1, &indexBuffer); + gl->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); + gl->glBufferData(GL_ELEMENT_ARRAY_BUFFER, vecIndices.size() * sizeof(uint32_t), vecIndices.data(), GL_STATIC_DRAW); + + //We need to tell OpenGL how to understand the format of the vertex data + gl->glEnableVertexAttribArray(0); //We're talking about shader attribute '0' + gl->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(PositionMaterial), 0); //take the first 3 floats from every sizeof(decltype(vecVertices)::value_type) + + gl->glBindVertexArray(0); + + noOfIndices = vecIndices.size(); //Save this for the call to glDrawElements later } void OpenGLWidget::initializeGL() { - //We need GLEW to access recent OpenGL functionality - std::cout << "Initialising GLEW..."; - GLenum result = glewInit(); - if (result == GLEW_OK) + //'gl' will give us access to all the OpenGL functions + gl = context()->contextHandle()->versionFunctions(); + if(!gl) { - std::cout << "success" << std::endl; + std::cerr << "Could not obtain required OpenGL context version" << std::endl; + exit(EXIT_FAILURE); } - else + if(!gl->initializeOpenGLFunctions()) { - /* Problem: glewInit failed, something is seriously wrong. */ - std::cout << "failed" << std::endl; - std::cout << "Initialising GLEW failed with the following error: " << glewGetErrorString(result) << std::endl; + std::cerr << "Could not initialise OpenGL functions" << std::endl; exit(EXIT_FAILURE); } //Print out some information about the OpenGL implementation. std::cout << "OpenGL Implementation Details:" << std::endl; - if(glGetString(GL_VENDOR)) - std::cout << "\tGL_VENDOR: " << glGetString(GL_VENDOR) << std::endl; - if(glGetString(GL_RENDERER)) - std::cout << "\tGL_RENDERER: " << glGetString(GL_RENDERER) << std::endl; - if(glGetString(GL_VERSION)) - std::cout << "\tGL_VERSION: " << glGetString(GL_VERSION) << std::endl; - if(glGetString(GL_SHADING_LANGUAGE_VERSION)) - std::cout << "\tGL_SHADING_LANGUAGE_VERSION: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl; - - //Check our version of OpenGL is recent enough. - //We need at least 1.5 for vertex buffer objects, - if (!GLEW_VERSION_1_5) - { - std::cout << "Error: You need OpenGL version 1.5 to run this example." << std::endl; - exit(EXIT_FAILURE); - } + if(gl->glGetString(GL_VENDOR)) + std::cout << "\tGL_VENDOR: " << gl->glGetString(GL_VENDOR) << std::endl; + if(gl->glGetString(GL_RENDERER)) + std::cout << "\tGL_RENDERER: " << gl->glGetString(GL_RENDERER) << std::endl; + if(gl->glGetString(GL_VERSION)) + std::cout << "\tGL_VERSION: " << gl->glGetString(GL_VERSION) << std::endl; + if(gl->glGetString(GL_SHADING_LANGUAGE_VERSION)) + std::cout << "\tGL_SHADING_LANGUAGE_VERSION: " << gl->glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl; //Set up the clear colour - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClearDepth(1.0f); - - //Enable the depth buffer - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LEQUAL); - - //Anable smooth lighting - glEnable(GL_LIGHTING); - glEnable(GL_LIGHT0); - glShadeModel(GL_SMOOTH); - - //We'll be rendering with index/vertex arrays - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); + gl->glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + gl->glClearDepth(1.0f); + + gl->glEnable(GL_CULL_FACE); + gl->glEnable(GL_DEPTH_TEST); + gl->glDepthMask(GL_TRUE); + gl->glDepthFunc(GL_LEQUAL); + gl->glDepthRange(0.0, 1.0); + + if(!shader.addShaderFromSourceCode(QOpenGLShader::Vertex, R"( + #version 140 + + in vec4 position; //This will be the position of the vertex in model-space + + uniform mat4 cameraToClipMatrix; + uniform mat4 worldToCameraMatrix; + uniform mat4 modelToWorldMatrix; + + out vec4 worldPosition; //This is being passed to the fragment shader to calculate the normals + + void main() + { + worldPosition = modelToWorldMatrix * position; + vec4 cameraPosition = worldToCameraMatrix * worldPosition; + gl_Position = cameraToClipMatrix * cameraPosition; + } + )")) + { + std::cerr << shader.log().toStdString() << std::endl; + exit(EXIT_FAILURE); + } + + if(!shader.addShaderFromSourceCode(QOpenGLShader::Fragment, R"( + #version 130 + + in vec4 worldPosition; //Passed in from the vertex shader + + out vec4 outputColor; + + void main() + { + vec3 normal = normalize(cross(dFdy(worldPosition.xyz), dFdx(worldPosition.xyz))); + + float color = clamp(abs(dot(normalize(normal.xyz), vec3(0.9,0.1,0.5))), 0, 1); + outputColor = vec4(1.0, 0.5, color, 1.0); + } + )")) + { + std::cerr << shader.log().toStdString() << std::endl; + exit(EXIT_FAILURE); + } + + shader.bindAttributeLocation("position", 0); + + if(!shader.link()) + { + std::cerr << shader.log().toStdString() << std::endl; + exit(EXIT_FAILURE); + } + + shader.bind(); + + QMatrix4x4 worldToCameraMatrix{}; + worldToCameraMatrix.translate(0, 0, -50); //Move the camera back by 50 units + + shader.setUniformValue("worldToCameraMatrix", worldToCameraMatrix); + + shader.release(); } void OpenGLWidget::resizeGL(int w, int h) { //Setup the viewport - glViewport(0, 0, w, h); - - //Set up the projection matrix - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - float frustumSize = 32.0f; //Half the volume size - float aspect = static_cast(width()) / static_cast(height()); - glOrtho(frustumSize*aspect, -frustumSize*aspect, frustumSize, -frustumSize, 1.0, 1000); + gl->glViewport(0, 0, w, h); + + auto aspectRatio = w / (float)h; + float zNear = 1.0; + float zFar = 1000.0; + + QMatrix4x4 cameraToClipMatrix{}; + cameraToClipMatrix.frustum(-aspectRatio, aspectRatio, -1, 1, zNear, zFar); + + shader.bind(); + shader.setUniformValue("cameraToClipMatrix", cameraToClipMatrix); + shader.release(); } void OpenGLWidget::paintGL() { //Clear the screen - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - //Set up the viewing transformation - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glTranslatef(0.0f,0.0f,-100.0f); //Centre volume and move back - glRotatef(-m_xRotation, 0.0f, 1.0f, 0.0f); - glRotatef(-m_yRotation, 1.0f, 0.0f, 0.0f); - glTranslatef(-32.0f,-32.0f,-32.0f); //Centre volume and move back - - //Bind the index buffer - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); - - //Bind the vertex buffer - glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); - glVertexPointer(3, GL_FLOAT, sizeof(PositionMaterialNormal), 0); - glNormalPointer(GL_FLOAT, sizeof(PositionMaterialNormal), (GLvoid*)12); - - glDrawRangeElements(GL_TRIANGLES, m_uBeginIndex, m_uEndIndex-1, m_uEndIndex - m_uBeginIndex, GL_UNSIGNED_INT, 0); + gl->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - GLenum errCode = glGetError(); + QMatrix4x4 modelToWorldMatrix{}; + modelToWorldMatrix.rotate(m_xRotation, 0, 1, 0); //rotate around y-axis + modelToWorldMatrix.rotate(m_yRotation, 1, 0, 0); //rotate around x-axis + modelToWorldMatrix.translate(-32, -32, -32); //centre the model on the origin + + shader.bind(); + + shader.setUniformValue("modelToWorldMatrix", modelToWorldMatrix); //Update to the latest camera matrix + + gl->glBindVertexArray(vertexArrayObject); + + gl->glDrawElements(GL_TRIANGLES, noOfIndices, GL_UNSIGNED_INT, 0); + + gl->glBindVertexArray(0); + + shader.release(); + + GLenum errCode = gl->glGetError(); if(errCode != GL_NO_ERROR) { - //What has replaced getErrorString() in the latest OpenGL? - std::cout << "OpenGL Error: " << errCode << std::endl; + std::cerr << "OpenGL Error: " << errCode << std::endl; } } diff --git a/examples/Basic/OpenGLWidget.h b/examples/Basic/OpenGLWidget.h index 011b9734..08ff3cc7 100644 --- a/examples/Basic/OpenGLWidget.h +++ b/examples/Basic/OpenGLWidget.h @@ -26,9 +26,12 @@ distribution. #include "PolyVoxCore/SurfaceMesh.h" -#include "glew/glew.h" +#include #include +#include +#include +#include class OpenGLWidget : public QGLWidget { @@ -40,8 +43,8 @@ public: void mouseMoveEvent(QMouseEvent* event); void mousePressEvent(QMouseEvent* event); - //Convert a SrfaceMesh to OpenGL index/vertex buffers - void setSurfaceMeshToRender(const PolyVox::SurfaceMesh& surfaceMesh); + //Convert a SurfaceMesh to OpenGL index/vertex buffers + void setSurfaceMeshToRender(const PolyVox::SurfaceMesh& surfaceMesh); protected: //Qt OpenGL functions @@ -51,11 +54,13 @@ protected: private: //Index/vertex buffer data - GLuint m_uBeginIndex; - GLuint m_uEndIndex; GLuint noOfIndices; GLuint indexBuffer; GLuint vertexBuffer; + GLuint vertexArrayObject; + + QOpenGLShaderProgram shader; + QOpenGLFunctions_3_1* gl; //Mouse data QPoint m_LastFrameMousePos; diff --git a/examples/Basic/main.cpp b/examples/Basic/main.cpp index 64097731..9a3dac94 100644 --- a/examples/Basic/main.cpp +++ b/examples/Basic/main.cpp @@ -23,7 +23,7 @@ freely, subject to the following restrictions: #include "OpenGLWidget.h" -#include "PolyVoxCore/CubicSurfaceExtractorWithNormals.h" +#include "PolyVoxCore/CubicSurfaceExtractor.h" #include "PolyVoxCore/MarchingCubesSurfaceExtractor.h" #include "PolyVoxCore/SurfaceMesh.h" #include "PolyVoxCore/SimpleVolume.h" @@ -78,10 +78,10 @@ int main(int argc, char *argv[]) createSphereInVolume(volData, 30); //A mesh object to hold the result of surface extraction - SurfaceMesh mesh; + SurfaceMesh mesh; //Create a surface extractor. Comment out one of the following two lines to decide which type gets created. - CubicSurfaceExtractorWithNormals< SimpleVolume > surfaceExtractor(&volData, volData.getEnclosingRegion(), &mesh); + CubicSurfaceExtractor< SimpleVolume > surfaceExtractor(&volData, volData.getEnclosingRegion(), &mesh); //MarchingCubesSurfaceExtractor< SimpleVolume > surfaceExtractor(&volData, volData.getEnclosingRegion(), &mesh); //Execute the surface extractor.