Commit 5ecf516c authored by Bastian Krayer's avatar Bastian Krayer
Browse files

added missing spline surface base

parent 4d885737
cmake_minimum_required(VERSION 3.1)
include(${CMAKE_MODULE_PATH}/DefaultExecutable.cmake)
\ No newline at end of file
#include "SplineSurface.h"
SplineSurface::SplineSurface(std::vector<glm::vec3> cpoints, int numSamplesX,
int numSamplesY)
: m_controlPoints(std::move(cpoints)) {}
const std::vector<glm::vec3>& SplineSurface::getVertices() {
return m_vertices;
}
const std::vector<glm::vec3>& SplineSurface::getControlPoints() {
return m_controlPoints;
}
\ No newline at end of file
#ifndef BEZIERSURFACE_H
#define BEZIERSURFACE_H
#include <glm/glm.hpp>
#include <memory>
#include <vector>
class SplineSurface {
public:
SplineSurface(std::vector<glm::vec3> cpoints, int numSamplesX,
int numSamplesY);
const std::vector<glm::vec3>& getVertices();
const std::vector<glm::vec3>& getControlPoints();
protected:
std::vector<glm::vec3> m_controlPoints;
std::vector<glm::vec3> m_vertices;
};
#endif // BEZIERSURFACE_H
CVK_2
\ No newline at end of file
#include "SplineSurface.h"
#include <CVK_2/CVK_GlfwBase.h>
#include <CVK_2/CVK_ShaderSet.h>
#include <CVK_2/CVK_Trackball.h>
#include <memory>
constexpr int num_samples = 200; // number of vertices
constexpr int k = 3; // order
constexpr int width = 800; // window width
constexpr int height = 800; // window height
class ExSplineSurface : public CVK::GlfwBase {
public:
ExSplineSurface();
~ExSplineSurface();
void display();
private:
void render(SplineSurface& surface, const glm::mat4& projection,
const glm::mat4& cam);
std::unique_ptr<CVK::ShaderSet> mShaderProgram;
GLint mColorHandle;
GLint mProjectionHandle;
GLint mViewHandle;
GLuint mVbos[3];
};
int main() {
ExSplineSurface demo;
demo.display();
return 0;
}
// Implementation
ExSplineSurface::ExSplineSurface() {
m_window = glfwCreateWindow(width, height, "Spline-Surface", 0, 0);
glfwSetWindowPos(m_window, 600, 50);
glfwMakeContextCurrent(m_window);
glfwSetWindowUserPointer(m_window, this);
// init opengl
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
std::cerr << "Failed to initialize OpenGL context" << std::endl;
throw std::runtime_error("Failed to initialize OpenGL context");
}
glClearColor(1, 1, 1, 1);
glCullFace(GL_NONE);
glEnable(GL_DEPTH_TEST);
// compile a shader program
std::vector<std::string> shadernames = {
CVK::State::getInstance()->getShaderPath() +
"/BSplines/bsplines_cam.vert",
CVK::State::getInstance()->getShaderPath() +
"/BSplines/phong_bezier.frag"};
mShaderProgram = std::make_unique<CVK::ShaderSet>(
VERTEX_SHADER_BIT | FRAGMENT_SHADER_BIT, shadernames);
// use the shader program
mShaderProgram->useProgram();
mColorHandle =
glGetUniformLocation(mShaderProgram->getProgramID(), "color");
mProjectionHandle =
glGetUniformLocation(mShaderProgram->getProgramID(), "projection");
mViewHandle = glGetUniformLocation(mShaderProgram->getProgramID(), "view");
GLuint vaoHandle;
glGenVertexArrays(1, &vaoHandle);
glBindVertexArray(vaoHandle);
glGenBuffers(3, mVbos);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
ExSplineSurface::~ExSplineSurface() { glDeleteBuffers(2, mVbos); }
void ExSplineSurface::display() {
// controlpoints
std::vector<glm::vec3> cpoints;
// controlpoints
std::vector<glm::vec4> cpointsTrim;
// create control points beware that the order matters!
cpoints.push_back(glm::vec3(-0.5, -0.25, -0.5));
cpoints.push_back(glm::vec3(-0.5, 0.5, -0.25));
cpoints.push_back(glm::vec3(-0.5, -0.25, 0.0));
cpoints.push_back(glm::vec3(0, 0, -0.5));
cpoints.push_back(glm::vec3(0, 0.5, -0.25));
cpoints.push_back(glm::vec3(0, 0, 0.0));
cpoints.push_back(glm::vec3(0.5, -0.25, -0.5));
cpoints.push_back(glm::vec3(0.5, 0.5, -0.25));
cpoints.push_back(glm::vec3(0.5, -0.25, 0.0));
// create control points // z==0 make sure we are in range [0 1]
cpointsTrim.push_back(glm::vec4(0.5, 0, 0.0, 1.0));
cpointsTrim.push_back(glm::vec4(0, 0.3, 0.0, 3.0));
cpointsTrim.push_back(glm::vec4(0.25, 0.5, 0.0, 3.0));
cpointsTrim.push_back(glm::vec4(0.5, 0.25, 0.0, 2));
cpointsTrim.push_back(glm::vec4(0.5, 0.25, 0.0, 2));
cpointsTrim.push_back(glm::vec4(0.5, 0.25, 0.0, 2));
cpointsTrim.push_back(glm::vec4(0.75, 0.5, 0.0, 3));
cpointsTrim.push_back(glm::vec4(1, 0.3, 0.0, 3));
cpointsTrim.push_back(glm::vec4(0.5, 0, 0.0, 1.0));
SplineSurface splineSurface(cpoints, num_samples, num_samples);
// trackball camera
auto proj = std::make_shared<CVK::Perspective>(
glm::radians(75.0f), float(width) / float(height), 0.1, 10);
CVK::Trackball cam(m_window, width, height, proj);
cam.setRadius(1);
double lastTime = glfwGetTime();
double deltaTime = 0.0;
double nowTime = 0.0;
while (!glfwWindowShouldClose(m_window)) {
nowTime = glfwGetTime();
deltaTime = nowTime - lastTime;
lastTime = nowTime;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
cam.update(deltaTime);
render(splineSurface, cam.getProjection()->getProjMatrix(),
cam.getView());
// show what's been drawn
glfwSwapBuffers(m_window);
glfwPollEvents();
}
}
void ExSplineSurface::render(SplineSurface& surface,
const glm::mat4& projection,
const glm::mat4& cam) {
glm::vec3 black = glm::vec3(0.0, 0.0, 0.0);
glm::vec3 red = glm::vec3(1.0, 0.0, 0.0);
mShaderProgram->useProgram();
// update camera info
glUniformMatrix4fv(mProjectionHandle, 1, false, glm::value_ptr(projection));
glUniformMatrix4fv(mViewHandle, 1, false, glm::value_ptr(cam));
// draw control points
glPointSize(15.0f);
glUniform3fv(mColorHandle, 1, glm::value_ptr(black));
glBindBuffer(GL_ARRAY_BUFFER, mVbos[0]);
glBufferData(GL_ARRAY_BUFFER,
sizeof(glm::vec3) * surface.getControlPoints().size(),
surface.getControlPoints().data(), GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glDrawArrays(GL_POINTS, 0, surface.getControlPoints().size());
// draw curve points
if (!surface.getVertices().empty()) {
glPointSize(5.0f);
glUniform3fv(mColorHandle, 1, glm::value_ptr(red));
glBindBuffer(GL_ARRAY_BUFFER, mVbos[1]);
glBufferData(GL_ARRAY_BUFFER,
sizeof(glm::vec3) * surface.getVertices().size(),
surface.getVertices().data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glDrawArrays(GL_POINTS, 0, surface.getVertices().size());
// unbind the VBO, we don't need it anymore
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
}
#include "BSpline.h"
BSpline::BSpline(std::vector<glm::vec4> cpoints, int k)
: m_controlPoints(std::move(cpoints)), m_k(k) {}
BSpline::~BSpline(void) {}
glm::vec4 BSpline::evaluate(float t) {
glm::vec4 result;
for (unsigned int i = 0; i < m_controlPoints.size(); i++) {
result += m_controlPoints[i] * coxDeBoor(t, i, m_k);
}
return result;
}
float BSpline::coxDeBoor(float t, int i, int k) {
if (k == 1) {
if (t >= m_knotVector[i] && t < m_knotVector[i + 1]) {
return 1.0;
} else {
return 0.0;
}
} else {
float nom0 = ((t - m_knotVector[i]) * coxDeBoor(t, i, k - 1));
float denom0 = (m_knotVector[i + k - 1] - m_knotVector[i]);
float nom1 = ((m_knotVector[i + k] - t) * coxDeBoor(t, i + 1, k - 1));
float denom1 = (m_knotVector[i + k] - m_knotVector[i + 1]);
float frac0, frac1;
denom0 > 0.0 ? frac0 = nom0 / denom0 : frac0 = 0.0;
denom1 > 0.0 ? frac1 = nom1 / denom1 : frac1 = 0.0;
return frac0 + frac1;
}
}
void BSpline::setK(int val) { m_k = val; }
int BSpline::getK() const { return m_k; }
const std::vector<glm::vec4>& BSpline::getVertices() { return m_vertices; }
const std::vector<glm::vec4>& BSpline::getControlPoints() {
return m_controlPoints;
}
\ No newline at end of file
#ifndef BSPLINE_H
#define BSPLINE_H
#include <glm/glm.hpp>
#include <iostream>
#include <list>
#include <vector>
class BSpline {
public:
BSpline(std::vector<glm::vec4> cpoints, int k);
virtual ~BSpline(void);
virtual bool initialize(int num) = 0;
void setK(int val);
int getK() const;
const std::vector<glm::vec4>& getVertices();
const std::vector<glm::vec4>& getControlPoints();
protected:
float coxDeBoor(float t, int i, int k);
virtual glm::vec4 evaluate(float t);
std::vector<glm::vec4> m_controlPoints;
std::vector<glm::vec4> m_vertices;
std::vector<float> m_knotVector;
int m_k = 0;
};
#endif
\ No newline at end of file
cmake_minimum_required(VERSION 3.1)
include(${CMAKE_MODULE_PATH}/DefaultExecutable.cmake)
\ No newline at end of file
#include "NURBS.h"
NURBS::NURBS(std::vector<glm::vec4> cpoints, int k, int numSamples)
: BSpline(std::move(cpoints), k) {
// create knot vector
int num_e = m_controlPoints.size() + k; // number of entries
for (int i = 0; i < num_e; i++) {
if (i < k)
m_knotVector.push_back(0);
else if (i < num_e - k)
m_knotVector.push_back(i - k + 1);
else
m_knotVector.push_back(num_e - 2 * k + 1);
}
initialize(numSamples);
}
bool NURBS::initialize(int num) {
float tStep = (m_knotVector.back() - m_knotVector.front()) /
static_cast<float>((num - 1));
float t = m_knotVector.front();
for (int i = 0; i < num; i++) {
m_vertices.push_back(evaluate(t));
t += tStep;
}
return true;
}
glm::vec4 NURBS::evaluate(float t) {
/* TODO: Aufgabe 1:
Schreiben Sie diese Methode, die den NURBS an der Stelle t evaluiert.
Beachten Sie, dass die NURBS die BSplines um Gewichte erweitern.
Hinweis: Sie finden die passende Formel in den Vorlesungsfolien.
Bedenken Sie, dass die homogenisierung automatisch stattfinden kann.*/
/*
glm::vec3 numerator = glm::vec3(0);
glm::vec4 result = glm::vec4(0);
float denominator = 0.f;
for(unsigned int i=0; i<m_controlPoints.size(); i++)
{
float n_ik = coxDeBoor(t, i, mK);
numerator += glm::vec3(m_controlPoints[i]) * m_controlPoints[i].w *
n_ik; denominator += m_controlPoints[i].w * n_ik;
}
result = glm::vec4(numerator / denominator,1);
*/
// OpenGL for homogenisation
glm::vec4 result = glm::vec4(0);
glm::vec4 B_i = glm::vec4(0);
for (unsigned int i = 0; i < m_controlPoints.size(); i++) {
B_i = m_controlPoints[i] * m_controlPoints[i].w;
B_i.w = m_controlPoints[i].w; // make sure we dont have w² as weight
result += B_i * coxDeBoor(t, i, m_k);
}
return result;
}
#ifndef NURBS_H
#define NURBS_H
#include "BSpline.h"
#include <glm/vec3.hpp>
#include <vector>
class NURBS : public BSpline {
public:
NURBS(std::vector<glm::vec4> cpoints, int k, int numSamples);
bool initialize(int num) override;
protected:
// we override the method as we have to consider the weights
glm::vec4 evaluate(float t) override;
};
#endif // NURBS
\ No newline at end of file
#include "SplineSurface.h"
#include <algorithm>
#include <limits>
SplineSurface::SplineSurface(std::vector<glm::vec3>& cpoints, int numSamplesX,
int numSamplesY, std::shared_ptr<NURBS> trimCurve)
: m_controlPoints(cpoints) {
// todo: test if trim curve is valid?!
m_trimCurve = std::move(trimCurve);
initialize(numSamplesX, numSamplesY);
}
bool SplineSurface::initialize(int numX, int numY) {
float tStepX = 1.0f / (numX - 1);
float tStepY = 1.0f / (numY - 1);
float tx = 0;
float ty = 0;
// iterate in X and Y direction (U/V in script)
for (int i = 0; i < numX; i++) {
tx = std::min(1.0f - std::numeric_limits<float>::epsilon(), tx);
ty = 0;
for (int j = 0; j < numY; j++) {
ty = std::min(1.0f - std::numeric_limits<float>::epsilon(), ty);
// ignore points in trim curve
if (!trimTest(ty, tx)) {
glm::vec3 position;
glm::vec4 normal;
evaluate(tx, ty, position, normal);
// alternative: define an evaluate method returning only the
// position using the decastlejau algorithm twice or applying
// the weight formula. Then compute the tangents with: Small
// offset value
// const float EPS = 1E-3;
// glm::vec3 t_u = evaluate(tx + EPS, ty) - position;
// glm::vec3 t_v = evaluate(tx, ty + EPS) - position;
// glm::vec3 normal = glm::normalize(glm::cross(t_u,_v));
m_vertices.push_back(position);
m_normals.push_back(normal);
}
ty += tStepY;
}
tx += tStepX;
}
return true;
}
void SplineSurface::evaluate(float t0, float t1, glm::vec3& position,
glm::vec4& normal) {
// The exercise demands a solution for 3x3 points.
// We chose to use a hard code variant as this provides
// an easier understanding of the underlying concepts
// load all points in the correct order
glm::vec3 p00 = m_controlPoints.at(0);
glm::vec3 p01 = m_controlPoints.at(1);
glm::vec3 p02 = m_controlPoints.at(2);
glm::vec3 p10 = m_controlPoints.at(3);
glm::vec3 p11 = m_controlPoints.at(4);
glm::vec3 p12 = m_controlPoints.at(5);
glm::vec3 p20 = m_controlPoints.at(6);
glm::vec3 p21 = m_controlPoints.at(7);
glm::vec3 p22 = m_controlPoints.at(8);
// calculate bezier-points in 1 direction
glm::vec3 B0 = deCasteljau(t0, p00, p01, p02);
glm::vec3 B1 = deCasteljau(t0, p10, p11, p12);
glm::vec3 B2 = deCasteljau(t0, p20, p21, p22);
// flip direction to calculate second tangent
glm::vec3 C0 = deCasteljau(t1, p00, p10, p20);
glm::vec3 C1 = deCasteljau(t1, p01, p11, p21);
glm::vec3 C2 = deCasteljau(t1, p02, p12, p22);
// calculate normal with cross product of tangents
glm::vec3 Bt01 = (1 - t1) * B0 + t1 * B1;
glm::vec3 Bt12 = (1 - t1) * B1 + t1 * B2;
glm::vec3 Ct01 = (1 - t0) * C0 + t0 * C1;
glm::vec3 Ct12 = (1 - t0) * C1 + t0 * C2;
glm::vec3 tangent0 = Bt01 - Bt12;
glm::vec3 tangent1 = Ct01 - Ct12;
normal = glm::vec4(glm::normalize(glm::cross(tangent0, tangent1)), 0);
// use generated points for the other direction
position = deCasteljau(t1, B0, B1, B2);
}
glm::vec3 SplineSurface::deCasteljau(float t, glm::vec3& p0, glm::vec3& p1,
glm::vec3& p2) {
glm::vec3 p01 = (1 - t) * p0 + t * p1;
glm::vec3 p12 = (1 - t) * p1 + t * p2;
return (1 - t) * p01 + t * p12;
}
bool SplineSurface::trimTest(float u, float v) {
int intersectCount = 0;
glm::vec2 o = glm::vec2(u, v);
for (int i = 0; i < m_trimCurve->getVertices().size() - 1; i++) {
glm::vec4 tmpA = m_trimCurve->getVertices().at(i);
glm::vec4 tmpB = m_trimCurve->getVertices().at(i + 1);
glm::vec2 p0 = glm::vec2(tmpA.x, tmpA.y);
glm::vec2 p1 = glm::vec2(tmpB.x, tmpB.y);
// don't forget that the nurbs are not homogenized
p0 /= tmpA.w;
p1 /= tmpB.w;
glm::vec2 v1 = o - p0;
glm::vec2 v2 = p1 - p0;
// we are free to chose the ray direction..
glm::vec2 rayNormal = glm::vec2(0, -1);
float d = glm::dot(v2, rayNormal);
// avoid division with zero
if (glm::abs(d) < 0.000001)
continue;
float t1 = cross2D(v2, v1) / d;
float t2 = glm::dot(v1, rayNormal) / d;
if (t1 >= 0 && (t2 >= 0 && t2 <= 1))
intersectCount++;
}
// we have just one trim curve without a hierarchy
if (intersectCount % 2 != 0)
return true;
else
return false;
}
float SplineSurface::cross2D(const glm::vec2& u, const glm::vec2& v) {
return u.x * v.y - u.y * v.x;
}
const std::vector<glm::vec3>& SplineSurface::getVertices() {
return m_vertices;
}
const std::vector<glm::vec4>& SplineSurface::getNormals() { return m_normals; }
const std::vector<glm::vec3>& SplineSurface::getControlPoints() {
return m_controlPoints;
}
\ No newline at end of file
#ifndef BEZIERSURFACE_H
#define BEZIERSURFACE_H
#include "NURBS.h"
#include <glm/glm.hpp>
#include <memory>
#include <vector>
class SplineSurface {
public:
SplineSurface(std::vector<glm::vec3>& cpoints, int numSamplesX,
int numSamplesY, std::shared_ptr<NURBS> trimCurve);
bool initialize(int numX, int numY);
const std::vector<glm::vec3>& getVertices();
const std::vector<glm::vec4>& getNormals();
const std::vector<glm::vec3>& getControlPoints();
protected:
std::vector<glm::vec3> m_controlPoints;