Part of the Khronos Group
OpenGL.org

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Results 1 to 2 of 2

Thread: Better approach to rendering perlin noise to a texture?

  1. #1
    Junior Member Newbie
    Join Date
    Nov 2017
    Posts
    6

    Better approach to rendering perlin noise to a texture?

    The vertex shader takes two input attributes: the first is the upper left corner vertex position and the second is the four gradients at the corner of each cell. The geometry shader takes each point and constructs two triangles to form the cell and passes the gradients through to the fragment shader to calculate the brightness of the fragment.

    Is there a more efficient approach?

    Code:
    Code :
    #pragma once
     
    #include <iostream>
    #include <vector>
    #include <chrono>
    #include <random>
    #include <glm.hpp>
    #include <GL/glew.h>
    #include <gtc/type_ptr.hpp>
    #include <gtc/matrix_transform.hpp>
    #include "../shaders/geometryShader.hpp"
     
    class PerlinNoise {
    	GLuint xSub, ySub;
    	GLfloat width, height;
    	GLuint VAO, VBO, GBO;
    	GeometryShader shader;
    	std::vector<GLfloat> xy, uv, quadVerts, quadGrads;
     
    public:
    	PerlinNoise(GLfloat width, GLfloat height, GLuint xSub, GLuint ySub) : shader("shaders/perlin/perlin.vs", "shaders/perlin/perlin.gs", "shaders/perlin/perlin.fs") {
    		this->width = width;
    		this->height = height;
    		this->xSub = xSub;
    		this->ySub = ySub;
     
    		genGrid();
    		initVAO();
    	}
     
    	void initVAO() {
    		glGenVertexArrays(1, &VAO);
    		glGenBuffers(1, &VBO);
    		glGenBuffers(1, &GBO);
     
    		glBindVertexArray(VAO);
     
    		glBindBuffer(GL_ARRAY_BUFFER, VBO);
    		glBufferData(GL_ARRAY_BUFFER, quadVerts.size() * sizeof(GLfloat), &quadVerts.front(), GL_DYNAMIC_DRAW);
    		glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (GLvoid*)0);
    		glEnableVertexAttribArray(0);
     
    		glBindBuffer(GL_ARRAY_BUFFER, GBO);
    		glBufferData(GL_ARRAY_BUFFER, quadGrads.size() * sizeof(GLfloat), &quadGrads.front(), GL_DYNAMIC_DRAW);
    		glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);
    		glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(2 * sizeof(GLfloat)));
    		glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(4 * sizeof(GLfloat)));
    		glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));
    		glEnableVertexAttribArray(1);
    		glEnableVertexAttribArray(2);
    		glEnableVertexAttribArray(3);
    		glEnableVertexAttribArray(4);
    		glBindBuffer(GL_ARRAY_BUFFER, 0);
     
    		glBindVertexArray(0);
    	}
     
    	void genGrid() {
    		GLfloat xSpacing = width / xSub,
    			ySpacing = height / ySub;
     
    		unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
    		std::default_random_engine generator(seed);
    		std::uniform_real_distribution<GLfloat> distribution(-1.0, 1.0);
    		for (GLuint j = 0; j < (ySub + 1); j++) {
    			for (GLuint i = 0; i < (xSub + 1); i++) {
    				glm::vec2 grad = glm::normalize(glm::vec2(distribution(generator), distribution(generator)));
    				xy.push_back(i*xSpacing - 1.0f);
    				xy.push_back(1.0f - j*ySpacing);
    				uv.push_back(grad.x);
    				uv.push_back(grad.y);
    			}
    		}
     
    		for (int j = 0; j < ySub; j++) {
    			for (int i = 0; i < xSub; i++) {
    				GLuint index1 = (i + j*(xSub + 1));
    				GLuint index2 = (i + j*(xSub + 1) + 1);
    				GLuint index3 = (i + (j + 1)*(xSub + 1));
    				GLuint index4 = (i + (j + 1)*(xSub + 1) + 1);
     
    				quadVerts.push_back(xy[2 * index1]);
    				quadVerts.push_back(xy[2 * index1 + 1]);
     
    				quadGrads.push_back(uv[2 * index1]);
    				quadGrads.push_back(uv[2 * index1 + 1]);
     
    				quadGrads.push_back(uv[2 * index2]);
    				quadGrads.push_back(uv[2 * index2 + 1]);
     
    				quadGrads.push_back(uv[2 * index3]);
    				quadGrads.push_back(uv[2 * index3 + 1]);
     
    				quadGrads.push_back(uv[2 * index4]);
    				quadGrads.push_back(uv[2 * index4 + 1]);
    			}
    		}
    	}
     
    	void render() {
    		glDisable(GL_CULL_FACE);
    		shader.use();
    		glUniform1f(glGetUniformLocation(shader.program, "xSpacing"), width / xSub);
    		glUniform1f(glGetUniformLocation(shader.program, "ySpacing"), height / ySub);
    		glBindVertexArray(VAO);
    		glDrawArrays(GL_POINTS, 0, quadVerts.size() / 2);
    		glBindVertexArray(0);
    		glEnable(GL_CULL_FACE);
    	}
     
    	void cleanUp() {
    		glDeleteVertexArrays(1, &VAO);
    		glDeleteBuffers(1, &VBO);
    		glDeleteBuffers(1, &GBO);
    	}
    };

    My shader code:

    Code :
    //Vertex Shader
    #version 440 core
     
    layout(location = 0) in vec2 position;
    layout(location = 1) in vec2 gradient[4];
     
    out vec2 gradient_pass[4];
     
    void main(){
    	gradient_pass = gradient;
    	gl_Position = vec4(position, -1.0,1.0);
    }
     
    //Geometry Shader
    #version 440 core
     
    layout (points) in;
    layout (triangle_strip, max_vertices = 4) out;
     
    uniform float xSpacing;
    uniform float ySpacing;
     
    in vec2 gradient_pass[][4];
     
    out vec2 fragPos;
    flat out vec2 gradient_fs_pass[4];
     
    void main(){
    	vec4 corner[4];
    	corner[0] = gl_in[0].gl_Position;
    	corner[1] = gl_in[0].gl_Position + vec4(xSpacing, 0.0, 0.0, 0.0);
    	corner[2] = gl_in[0].gl_Position + vec4(0.0,-ySpacing, 0.0, 0.0);
    	corner[3] = gl_in[0].gl_Position + vec4(xSpacing, -ySpacing, 0.0, 0.0);
     
    	gl_Position = corner[0];
    	fragPos = vec2(0.0,0.0);
    	gradient_fs_pass = gradient_pass[0];
    	EmitVertex();
     
    	gl_Position = corner[1];
    	fragPos = vec2(1.0,0.0);
    	gradient_fs_pass = gradient_pass[0];
    	EmitVertex();
     
    	gl_Position = corner[2];
    	fragPos = vec2(0.0,1.0);
    	gradient_fs_pass = gradient_pass[0];
    	EmitVertex();
     
    	gl_Position = corner[3];
    	fragPos = vec2(1.0,1.0);
    	gradient_fs_pass = gradient_pass[0];
    	EmitVertex();
    	EndPrimitive();
    }
     
    //Fragment Shader
    #version 440 core
     
    in vec2 fragPos;
    flat in vec2 gradient_fs_pass[4];
     
    out vec4 color;
     
    float smootherstep(float t){
    	return 6*pow(t,5) - 15*pow(t,4)+10*pow(t,3);
    }
     
    void main(){
    	vec2 s11 = vec2(fragPos.x, -fragPos.y);
    	vec2 s12 = vec2(fragPos.x-1, -fragPos.y);
    	vec2 s21 = vec2(fragPos.x, 1-fragPos.y);
    	vec2 s22 = vec2(fragPos.x-1, 1-fragPos.y);
     
    	float a11 = (dot(s11, gradient_fs_pass[0])+1.0)/2.0;
    	float a12 = (dot(s12, gradient_fs_pass[1])+1.0)/2.0;
    	float a21 = (dot(s21, gradient_fs_pass[2])+1.0)/2.0;
    	float a22 = (dot(s22, gradient_fs_pass[3])+1.0)/2.0;
     
    	float aTop = a11 + smootherstep(fragPos.x)*(a12-a11);
    	float aBot = a21 + smootherstep(fragPos.x)*(a22-a21);
    	float a = aTop + smootherstep(fragPos.y)*(aBot-aTop);
     
    	color = vec4(a,a,a,1.0);
    }

  2. #2
    Senior Member OpenGL Guru
    Join Date
    Jun 2013
    Posts
    2,519
    Quote Originally Posted by cmcrook View Post
    The vertex shader takes two input attributes: the first is the upper left corner vertex position and the second is the four gradients at the corner of each cell. The geometry shader takes each point and constructs two triangles to form the cell and passes the gradients through to the fragment shader to calculate the brightness of the fragment.

    Is there a more efficient approach?
    Normally, Perlin noise is formed as a rectangular grid of cells, where the gradient at each vertex is shared by the four cells sharing that vertex. In which case, you may as well ditch the geometry shader and just render either a grid of quads (triangle pairs) with a gradient per vertex, or a single quad with the gradients stored in a texture (the fragment shader divides the surface coordinates by the cell size, with the quotient identifying the cell and the remainder the position within the cell).

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •