Merge Terrain Update

Terrain merge
This commit is contained in:
Linloir 2022-12-19 21:36:25 +08:00 committed by GitHub
commit bca72138a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 326 additions and 30 deletions

2
.gitignore vendored
View File

@ -364,3 +364,5 @@ FodyWeavers.xsd
/FinalProject/temp/shaders
/Models
/Terrains
/terrain

View File

@ -135,6 +135,7 @@
<ClCompile Include="sidebar.cpp" />
<ClCompile Include="skybox.cpp" />
<ClCompile Include="slider.cpp" />
<ClCompile Include="terrain.cpp" />
<ClCompile Include="texture.cpp" />
<ClCompile Include="vao.cpp" />
<ClCompile Include="vbo.cpp" />
@ -170,6 +171,7 @@
<QtMoc Include="settingpage.h" />
<ClInclude Include="skybox.h" />
<QtMoc Include="slider.h" />
<ClInclude Include="terrain.h" />
<ClInclude Include="texture.h" />
<ClInclude Include="utils.h" />
<ClInclude Include="vbo.h" />

View File

@ -242,6 +242,9 @@
<ClInclude Include="hitrecord.h">
<Filter>Header Files\OpenGL Abstractions</Filter>
</ClInclude>
<ClInclude Include="terrain.h">
<Filter>Header Files\OpenGL Abstractions</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<QtMoc Include="mainwindow.h">

View File

@ -40,6 +40,8 @@ SceneViewer::SceneViewer(QWidget* parent)
extractShaderResource("fragmentshader.glsl");
extractShaderResource("skyboxvertexshader.glsl");
extractShaderResource("skyboxfragmentshader.glsl");
extractShaderResource("terrainvertexshader.glsl");
extractShaderResource("terrainfragmentshader.glsl");
extractShaderResource("boundfragmentshader.glsl");
extractShaderResource("boundvertexshader.glsl");
}
@ -69,6 +71,7 @@ void SceneViewer::extractShaderResource(const QString& shaderName) {
Renderable* SceneViewer::hitTest(const Ray& ray) {
HitRecord newRecord = HitRecord();
Renderable* newObject = nullptr;
// Object hit test
for (int i = 0; i < _objects.size(); i++) {
Logger::debug("Testing object " + std::to_string(i));
Renderable* obj = _objects[i];
@ -88,6 +91,18 @@ Renderable* SceneViewer::hitTest(const Ray& ray) {
newObject = obj;
}
}
// Terrain hit test
HitRecord hitRecord = _terrain->hit(ray);
if (hitRecord.hitted()) {
Logger::debug("Hitted terrain");
}
else {
Logger::debug("Missed terrain");
}
if (hitRecord.hitted() && hitRecord.t() < newRecord.t()) {
newRecord = hitRecord;
newObject = nullptr;
}
_hitRecord = newRecord;
return newObject;
}
@ -132,6 +147,16 @@ void SceneViewer::initializeGL() {
skyVertexShader.dispose();
skyFragmentShader.dispose();
_terrainShader.ensureInitialized();
Logger::info("Terrain Shader initialized");
VertexShader terrainVertexShader("./temp/shaders/terrainvertexshader.glsl");
FragmentShader terrainFragmentShader("./temp/shaders/terrainfragmentshader.glsl");
_terrainShader.attachShader(terrainVertexShader);
_terrainShader.attachShader(terrainFragmentShader);
terrainVertexShader.dispose();
terrainFragmentShader.dispose();
_dirLight = new DirLight();
_camera.setPosition(glm::vec3(0.0f, 0.0f, 10.0f));
@ -146,11 +171,35 @@ void SceneViewer::paintGL() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
_shaderProgram.bind();
// Set view and projection matrices
glm::mat4 view = _camera.viewMatrix();
glm::mat4 projection = _camera.projectionMatrix((float)width() / (float)height());
// Sky Box Render ---------------------------------------------------
if (_sky != nullptr) {
_skyShader.bind();
_skyShader.setUniform("view", glm::mat4(glm::mat3(view)));
_skyShader.setUniform("projection", projection);
_sky->render();
_skyShader.unbind();
}
// ------------------------------------------------------------------
// Terrain Render ---------------------------------------------------
if (_terrain != nullptr) {
_terrainShader.bind();
_terrainShader.setUniform("view", view);
_terrainShader.setUniform("projection", projection);
_terrainShader.setUniform("model", _terrain->modelMatrix());
_terrainShader.setUniform("texture1", 2);
_terrain->render();
_terrainShader.unbind();
}
// ------------------------------------------------------------------
// Renderable Render ------------------------------------------------
_shaderProgram.bind();
_shaderProgram.setUniform("view", view);
_shaderProgram.setUniform("projection", projection);
_shaderProgram.setUniform("viewPos", _camera.position());
@ -158,6 +207,7 @@ void SceneViewer::paintGL() {
int pointLights = 0;
int spotLights = 0;
// Update lights
for (auto object : _objects) {
if (object->hasLight()) {
ScopedLight light = object->transformedLight();
@ -179,6 +229,7 @@ void SceneViewer::paintGL() {
_shaderProgram.setUniform("dirlightnr", _dirLight != nullptr ? 1 : 0);
// Render objects
for (auto object : _objects) {
if (object == _pressedObject) {
_shaderProgram.setUniform("selColor", glm::vec3(0.22f));
@ -196,7 +247,9 @@ void SceneViewer::paintGL() {
}
_shaderProgram.unbind();
// ------------------------------------------------------------------
// Bound box render -------------------------------------------------
if (_selectedObject != nullptr && !_hideBound) {
_boundShader.bind();
_boundShader.setUniform("view", view);
@ -211,14 +264,7 @@ void SceneViewer::paintGL() {
_hoveredObject->boundary().render();
_boundShader.unbind();
}
if (_sky != nullptr) {
_skyShader.bind();
_skyShader.setUniform("view", glm::mat4(glm::mat3(view)));
_skyShader.setUniform("projection", projection);
_sky->render();
_skyShader.unbind();
}
// ------------------------------------------------------------------
}
void SceneViewer::mousePressEvent(QMouseEvent* event) {
@ -523,13 +569,13 @@ void SceneViewer::updateSetting(QPair<QString, QString> setting) {
}
}
else if (setting.first == "terrain") {
//if (_terrain != nullptr) {
// delete _terrain;
// _terrain = nullptr;
//}
//if (!setting.second.isEmpty()) {
// _terrain = new Terrain(setting.second.toStdString());
//}
if (_terrain != nullptr) {
delete _terrain;
_terrain = nullptr;
}
if (!setting.second.isEmpty()) {
_terrain = new Terrain(setting.second.toStdString());
}
}
else if (setting.first == "dirLight") {
if (setting.second == "true") {

View File

@ -15,6 +15,7 @@
#include "utils.h"
#include "logger.h"
#include "skybox.h"
#include "terrain.h"
class SceneViewer : public QOpenGLWidget, protected QOpenGLFunctions
{
@ -29,10 +30,13 @@ private:
// Dir light
bool _dirLightOn = false;
DirLight* _dirLight = nullptr;
// Terrain
Terrain* _terrain;
// Shader program for objects
ShaderProgram _shaderProgram = ShaderProgram::empty();
ShaderProgram _boundShader = ShaderProgram::empty();
ShaderProgram _skyShader = ShaderProgram::empty();
ShaderProgram _terrainShader = ShaderProgram::empty();
// Main camera
Camera _camera;
float _cameraMovementSpeed = 0.02f;

215
FinalProject/terrain.cpp Normal file
View File

@ -0,0 +1,215 @@
#include <STBImage/stb_image.h>
#include <vector>
#include <GLM/glm.hpp>
#include <GLM/gtc/type_ptr.hpp>
#include "terrain.h"
#include "utils.h"
#include "vertex.h"
Terrain::Terrain(std::string path){
// Convert '\ ' to '/' for Windows
std::replace(path.begin(), path.end(), '\\', '/');
stbi_set_flip_vertically_on_load(true);
unsigned char* data = stbi_load((path+"/heightmap.png").c_str(), &width, &height, &nrChannels, 0);
int rez = 1;
unsigned bytePerPixel = nrChannels;
for (int i = 0; i < height; i++)
{
std::vector<float> temp;
for (int j = 0; j < width; j++)
{
unsigned char* pixelOffset = data + (j + width * i) * bytePerPixel;
unsigned char y = pixelOffset[0];
// vertex
vertices.push_back(-height / 2.0f + height * i / (float)height); // vx
vertices.push_back((int)y * yScale - yShift); // vy
vertices.push_back(-width / 2.0f + width * j / (float)width); // vz
vertices.push_back((float)i);
vertices.push_back((float)j);
temp.push_back((int)y * yScale - yShift);
}
Point.push_back(temp);
}
stbi_image_free(data);
for (unsigned i = 0; i < height - 1; i += rez)
{
for (unsigned j = 0; j < width; j += rez)
{
for (unsigned k = 0; k < 2; k++)
{
indices.push_back(j + width * (i + k * rez));
}
}
}
numStrips = (height - 1) / rez;
numTrisPerStrip = (width / rez) * 2 - 2;
OPENGL_EXTRA_FUNCTIONS->glGenVertexArrays(1, &terrainVAO);
OPENGL_EXTRA_FUNCTIONS->glBindVertexArray(terrainVAO);
OPENGL_EXTRA_FUNCTIONS->glGenBuffers(1, &terrainVBO);
OPENGL_EXTRA_FUNCTIONS->glBindBuffer(GL_ARRAY_BUFFER, terrainVBO);
OPENGL_EXTRA_FUNCTIONS->glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW);
// position attribute
OPENGL_EXTRA_FUNCTIONS->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
OPENGL_EXTRA_FUNCTIONS->glEnableVertexAttribArray(0);
// texCoord attribute
OPENGL_EXTRA_FUNCTIONS->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(sizeof(float) * 3));
OPENGL_EXTRA_FUNCTIONS->glEnableVertexAttribArray(1);
OPENGL_EXTRA_FUNCTIONS->glGenBuffers(1, &terrainIBO);
OPENGL_EXTRA_FUNCTIONS->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, terrainIBO);
OPENGL_EXTRA_FUNCTIONS->glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned), indices.data(), GL_STATIC_DRAW);
//textureID = loadTexture2(texName, GL_REPEAT, GL_REPEAT, GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR);
tex = loadTexture(path + "/grass.jpg");
}
unsigned int Terrain::loadTexture(std::string path) {
unsigned int textureID;
OPENGL_FUNCTIONS->glGenTextures(1, &textureID);
OPENGL_FUNCTIONS->glBindTexture(GL_TEXTURE_2D, textureID);
stbi_set_flip_vertically_on_load(true);
int width, height, nrChannels;
unsigned char* data = stbi_load(path.c_str(), &width, &height, &nrChannels, 0);
if (data) {
GLenum format;
if (nrChannels == 1) {
format = GL_RED;
}
else if (nrChannels == 3) {
format = GL_RGB;
}
else if (nrChannels == 4) {
format = GL_RGBA;
}
else {
return 0;
}
OPENGL_FUNCTIONS->glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
OPENGL_FUNCTIONS->glGenerateMipmap(GL_TEXTURE_2D);
}
stbi_image_free(data);
return textureID;
}
void Terrain::render() {
// Set the texture wrapping parameters
OPENGL_FUNCTIONS->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture wrapping to GL_REPEAT (usually basic wrapping method)
OPENGL_FUNCTIONS->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// Set texture filtering parameters
OPENGL_FUNCTIONS->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
OPENGL_FUNCTIONS->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
OPENGL_EXTRA_FUNCTIONS->glActiveTexture(GL_TEXTURE2);
OPENGL_EXTRA_FUNCTIONS->glBindTexture(GL_TEXTURE_2D, tex);
OPENGL_EXTRA_FUNCTIONS->glBindVertexArray(terrainVAO);
for (unsigned strip = 0; strip < numStrips; strip++)
{
OPENGL_EXTRA_FUNCTIONS->glDrawElements(GL_TRIANGLE_STRIP, // primitive type
numTrisPerStrip + 2, // number of indices to render
GL_UNSIGNED_INT, // index data type
(void*)(sizeof(unsigned) * (numTrisPerStrip + 2) * strip)); // offset to starting index
}
OPENGL_EXTRA_FUNCTIONS->glBindVertexArray(0);
OPENGL_EXTRA_FUNCTIONS->glBindTexture(GL_TEXTURE_2D, 0);
}
float Terrain::GetHeight(float px, float pz) {
float fx = px + height / 2;
float fz = pz + width / 2;
int x = ((int)fx) % height;
int z = ((int)fz) % width;
int gx = (x + 1) % height;
int gz = (z + 1) % width;
float ans = (x - fx) * (Point[gx][gz] - Point[x][z]) + Point[x][z];
//float ans = Point[x][z];
return ans;
}
glm::vec3 Terrain::GetNormal(glm::vec3 pos) {
float fx = pos.x;
float fz = pos.z;
glm::vec3 point1(fx - 1, GetHeight(fx - 1, fz - 1), fz - 1);
glm::vec3 point2(fx + 1, GetHeight(fx + 1, fz + 1), fz + 1);
glm::vec3 l1 = pos - point1;
glm::vec3 l2 = point2 - point1;
glm::vec3 ans = glm::normalize(glm::cross(l1, l2));
return ans;
}
HitRecord Terrain::hit(const Ray& ray) {
glm::vec3 orig = ray.origin();
glm::vec3 dir = ray.direction();
if (orig.x >= width / 2 || orig.x <= -width / 2 || orig.z >= height / 2 || orig.z <= -height / 2) {
return HitRecord();
}
// A good ray step is half of the blockScale
glm::vec3 rayStep = dir;
glm::vec3 rayStartPosition = orig;
// Linear search - Loop until find a point inside and outside the terrain Vector3
glm::vec3 lastRayPosition = orig;
orig += rayStep;
float map_height = GetHeight(orig.x,orig.z);
while (orig.y > map_height)
{
lastRayPosition = orig;
orig += rayStep;
if (orig.x >= width / 2 || orig.x <= -width / 2 || orig.z >= height / 2 || orig.z <= -height / 2) {
return HitRecord();
}
map_height = GetHeight(orig.x, orig.z);
}
glm::vec3 startPosition = lastRayPosition;
glm::vec3 endPosition = orig;
// Binary search with 32 steps. Try to find the exact collision point
for (int i = 0; i < 32; i++)
{
// Binary search pass
glm::vec3 middlePoint = (startPosition + endPosition) * 0.5f;
if (middlePoint.y < height)
endPosition = middlePoint;
else
startPosition = middlePoint;
}
glm::vec3 position = (startPosition + endPosition) * 0.5f;
glm::vec3 normal = GetNormal(position);
// If t > 200, consider the ray as not hitted
float t = glm::length(position - rayStartPosition);
if (t > 200.0f)
return HitRecord();
else
return HitRecord(t, position, normal);
}

31
FinalProject/terrain.h Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#include <vector>
#include <string>
#include "utils.h"
#include "vertex.h"
#include "ray.h"
#include "hitrecord.h"
class Terrain {
private:
std::vector<float> vertices;
std::vector<unsigned int> indices;
unsigned int terrainVAO, terrainVBO, terrainIBO;
std::vector<std::vector<float>> Point;
float GetHeight(float px, float pz);
glm::vec3 GetNormal(glm::vec3 pos);
int width, height, nrChannels;
int numStrips, numTrisPerStrip;
int NUM_PATCH_PTS = 4;
unsigned rez = 20;
float yScale = 64.0f / 256.0f, yShift = 30.0f;
public:
unsigned int tex;
Terrain(std::string path);
void render();
unsigned int loadTexture(std::string path);
HitRecord hit(const Ray& ray);
glm::mat4 modelMatrix() const { return glm::mat4(1.0f); }
};

View File

@ -1,7 +1,6 @@
#version 330 core
#version 430 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
// texture sampler

View File

@ -1,9 +1,8 @@
#version 330 core
#version 430 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;
out vec3 ourColor;
layout (location = 1) in vec2 aTex;
out vec2 TexCoord;
uniform mat4 model;
@ -13,10 +12,5 @@ uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
ourColor = aColor;
TexCoord = vec2(aTexCoord.x, aTexCoord.y);
TexCoord = vec2(aTex.x, aTex.y);
}