|
@@ -0,0 +1,246 @@
|
|
|
+#include <GL/glew.h>
|
|
|
+#include <GLFW/glfw3.h>
|
|
|
+#include <stdbool.h>
|
|
|
+#include <stdio.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include <string.h>
|
|
|
+
|
|
|
+#define WIDTH 400
|
|
|
+#define HEIGHT 400
|
|
|
+
|
|
|
+static const GLchar* vertexShaderCode = "#version 430\n"
|
|
|
+ "layout(location = 0) in vec2 pos;"
|
|
|
+ ""
|
|
|
+ "out vec2 varTex;"
|
|
|
+ ""
|
|
|
+ "void main(void) {"
|
|
|
+ " varTex = (pos + 1.0) * 0.5;"
|
|
|
+ " gl_Position = vec4(pos, 0.0, 1.0);"
|
|
|
+ "}";
|
|
|
+
|
|
|
+static const GLchar* fragmentShaderCode =
|
|
|
+ "#version 430\n"
|
|
|
+ ""
|
|
|
+ "layout(binding = 0) uniform sampler2D samp;"
|
|
|
+ ""
|
|
|
+ "const vec4 heightColors[5] = {"
|
|
|
+ " vec4(0.0, 0.0, 0.7, 1.0),"
|
|
|
+ " vec4(0.0, 0.0, 1.0, 1.0),"
|
|
|
+ " vec4(0.0, 1.0, 0.0, 1.0),"
|
|
|
+ " vec4(0.6, 0.3, 0.0, 1.0),"
|
|
|
+ " vec4(0.3, 0.3, 0.3, 1.0),"
|
|
|
+ "};"
|
|
|
+ ""
|
|
|
+ "in vec2 varTex;"
|
|
|
+ ""
|
|
|
+ "out vec4 color;"
|
|
|
+ ""
|
|
|
+ "void main(void) {"
|
|
|
+ " float f = texture(samp, varTex).r;"
|
|
|
+ " //color = vec4(f, f, f, 1.0);\n"
|
|
|
+ " //color = heightColors[min(int(5 * f), 4)];\n"
|
|
|
+ " int a = int(min(floor(5 * f), 4));"
|
|
|
+ " int b = int(min(ceil(5 * f), 4));"
|
|
|
+ " color = mix(heightColors[a], heightColors[b], 5 * f - a);\n"
|
|
|
+ "}";
|
|
|
+
|
|
|
+static GLFWwindow* window = NULL;
|
|
|
+static GLuint vertexShader = 0;
|
|
|
+static GLuint fragmentShader = 0;
|
|
|
+static GLuint program = 0;
|
|
|
+static GLuint vertexArray = 0;
|
|
|
+static GLuint vertexBuffer = 0;
|
|
|
+static GLuint texture = 0;
|
|
|
+static GLfloat noise[WIDTH][HEIGHT];
|
|
|
+static unsigned long long seed = 0;
|
|
|
+
|
|
|
+static float nextFloat() {
|
|
|
+ seed = seed * 534492383lu + 31;
|
|
|
+ return (unsigned int)(seed >> 16) / (float)(-1u);
|
|
|
+}
|
|
|
+
|
|
|
+static void smooth(int size) {
|
|
|
+ float divider = 1.0f / (size * size);
|
|
|
+ static GLfloat newNoise[WIDTH][HEIGHT];
|
|
|
+ for(int x = 0; x < WIDTH; x++) {
|
|
|
+ for(int y = 0; y < HEIGHT; y++) {
|
|
|
+ float sum = 0.0f;
|
|
|
+ for(int mx = 0; mx < size; mx++) {
|
|
|
+ for(int my = 0; my < size; my++) {
|
|
|
+ sum += noise[(x + mx) % WIDTH][(y + my) % HEIGHT];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ newNoise[x][y] = sum * divider;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ memcpy(noise, newNoise, sizeof(newNoise));
|
|
|
+}
|
|
|
+
|
|
|
+static void normalize() {
|
|
|
+ float min = noise[0][0];
|
|
|
+ float max = noise[0][0];
|
|
|
+ for(int x = 0; x < WIDTH; x++) {
|
|
|
+ for(int y = 0; y < HEIGHT; y++) {
|
|
|
+ min = (min < noise[x][y] ? min : noise[x][y]);
|
|
|
+ max = (max > noise[x][y] ? max : noise[x][y]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ float divider = 1.0f / (max - min);
|
|
|
+ for(int x = 0; x < WIDTH; x++) {
|
|
|
+ for(int y = 0; y < HEIGHT; y++) {
|
|
|
+ noise[x][y] = (noise[x][y] - min) * divider;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void generateNoise() {
|
|
|
+ for(int x = 0; x < WIDTH; x++) {
|
|
|
+ for(int y = 0; y < HEIGHT; y++) {
|
|
|
+ noise[x][y] = nextFloat();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ smooth(2);
|
|
|
+ smooth(4);
|
|
|
+ smooth(8);
|
|
|
+ smooth(16);
|
|
|
+ smooth(32);
|
|
|
+ smooth(64);
|
|
|
+ normalize();
|
|
|
+
|
|
|
+ (void)normalize;
|
|
|
+ (void)smooth;
|
|
|
+}
|
|
|
+
|
|
|
+static bool checkShaderError(GLuint shader, const char* name) {
|
|
|
+ GLint status;
|
|
|
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
|
|
|
+ if(!status) {
|
|
|
+ printf("cannot compile %s shader:\n", name);
|
|
|
+ GLchar error[256];
|
|
|
+ glGetShaderInfoLog(shader, 256, NULL, error);
|
|
|
+ puts(error);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+static bool initShaders() {
|
|
|
+ vertexShader = glCreateShader(GL_VERTEX_SHADER);
|
|
|
+ glShaderSource(vertexShader, 1, &vertexShaderCode, NULL);
|
|
|
+ glCompileShader(vertexShader);
|
|
|
+ if(checkShaderError(vertexShader, "vertex")) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
|
|
|
+ glShaderSource(fragmentShader, 1, &fragmentShaderCode, NULL);
|
|
|
+ glCompileShader(fragmentShader);
|
|
|
+ if(checkShaderError(fragmentShader, "fragment")) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ program = glCreateProgram();
|
|
|
+ glAttachShader(program, vertexShader);
|
|
|
+ glAttachShader(program, fragmentShader);
|
|
|
+ glLinkProgram(program);
|
|
|
+
|
|
|
+ GLint linked;
|
|
|
+ glGetProgramiv(program, GL_LINK_STATUS, &linked);
|
|
|
+ if(!linked) {
|
|
|
+ puts("cannot link program:");
|
|
|
+ GLchar error[256];
|
|
|
+ glGetProgramInfoLog(program, 256, NULL, error);
|
|
|
+ puts(error);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ glUseProgram(program);
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+static void initVertexBuffer() {
|
|
|
+ glGenVertexArrays(1, &vertexArray);
|
|
|
+ glBindVertexArray(vertexArray);
|
|
|
+
|
|
|
+ glGenBuffers(1, &vertexBuffer);
|
|
|
+ glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
|
|
|
+
|
|
|
+ glVertexAttribPointer(0, 2, GL_FLOAT, false, sizeof(GLfloat) * 2, NULL);
|
|
|
+ glEnableVertexAttribArray(0);
|
|
|
+
|
|
|
+ GLfloat data[] = {-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f,
|
|
|
+ 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f};
|
|
|
+ glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
|
|
|
+}
|
|
|
+
|
|
|
+static void initTexture() {
|
|
|
+ glGenTextures(1, &texture);
|
|
|
+ glBindTexture(GL_TEXTURE_2D, texture);
|
|
|
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
|
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
|
+ generateNoise();
|
|
|
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, WIDTH, HEIGHT, 0, GL_RED, GL_FLOAT,
|
|
|
+ noise);
|
|
|
+}
|
|
|
+
|
|
|
+static bool init() {
|
|
|
+ if(glewInit() != GLEW_OK) {
|
|
|
+ puts("cannot init glew");
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ if(initShaders()) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ initVertexBuffer();
|
|
|
+ initTexture();
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+static bool createWindow() {
|
|
|
+ glfwWindowHint(GLFW_RESIZABLE, false);
|
|
|
+ window = glfwCreateWindow(WIDTH, HEIGHT, "Noise", NULL, NULL);
|
|
|
+ if(window == NULL) {
|
|
|
+ puts("cannot create window");
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ glfwMakeContextCurrent(window);
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+static void cleanUp() {
|
|
|
+ glDeleteBuffers(1, &vertexBuffer);
|
|
|
+ glDeleteVertexArrays(1, &vertexArray);
|
|
|
+ glDeleteTextures(1, &texture);
|
|
|
+ glDeleteShader(vertexShader);
|
|
|
+ glDeleteShader(fragmentShader);
|
|
|
+ glDeleteProgram(program);
|
|
|
+ if(window != NULL) {
|
|
|
+ glfwDestroyWindow(window);
|
|
|
+ }
|
|
|
+ glfwTerminate();
|
|
|
+}
|
|
|
+
|
|
|
+static void start() {
|
|
|
+ if(createWindow() || init()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ while(!glfwWindowShouldClose(window)) {
|
|
|
+ glClear(GL_COLOR_BUFFER_BIT);
|
|
|
+
|
|
|
+ glBindTexture(GL_TEXTURE_2D, texture);
|
|
|
+ glActiveTexture(GL_TEXTURE0);
|
|
|
+ glBindVertexArray(vertexArray);
|
|
|
+ glDrawArrays(GL_TRIANGLES, 0, 6);
|
|
|
+
|
|
|
+ glfwSwapBuffers(window);
|
|
|
+ glfwPollEvents();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int main() {
|
|
|
+ if(!glfwInit()) {
|
|
|
+ puts("glfw init error");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ start();
|
|
|
+ cleanUp();
|
|
|
+ return 0;
|
|
|
+}
|