diff --git a/main.c b/main.c index 01fde0b..259f26d 100755 --- a/main.c +++ b/main.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -103,12 +104,78 @@ void checkProgramLink(GLuint program) { } } +// ウィンドウの大きさとマウスの位置(シェーダーに渡す) +static int gWidth = 800; +static int gHeight = 600; + // ビューポートのリサイズコールバック void framebuffer_size_callback(GLFWwindow *window, int width, int height) { (void)window; + gWidth = width; + gHeight = height; glViewport(0, 0, width, height); } +// シェーダーをコンパイルし、成功したかどうかを返す +GLuint compileShader(GLenum type, const char *source) { + GLuint shader = glCreateShader(type); + glShaderSource(shader, 1, &source, NULL); + glCompileShader(shader); + + GLint success; + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (!success) { + checkShaderCompile(shader); + glDeleteShader(shader); + return 0; + } + return shader; +} + +// 頂点シェーダーとフラグメントシェーダーのファイルからプログラムを作る(失敗したら0) +GLuint buildProgram(const char *vertexPath, const char *fragmentPath) { + char *vertexSource = readShaderFile(vertexPath); + char *fragmentSource = readShaderFile(fragmentPath); + if (!vertexSource || !fragmentSource) { + free(vertexSource); + free(fragmentSource); + return 0; + } + +#ifdef __APPLE__ + // macOSはOpenGL 4.1までなので、「#version 450」を「#version 410」に書き換える + char *v; + if ((v = strstr(vertexSource, "#version 450"))) v[10] = '1'; + if ((v = strstr(fragmentSource, "#version 450"))) v[10] = '1'; +#endif + + GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vertexSource); + GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentSource); + free(vertexSource); + free(fragmentSource); + if (!vertexShader || !fragmentShader) { + if (vertexShader) glDeleteShader(vertexShader); + if (fragmentShader) glDeleteShader(fragmentShader); + return 0; + } + + GLuint program = glCreateProgram(); + glAttachShader(program, vertexShader); + glAttachShader(program, fragmentShader); + glLinkProgram(program); + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + + GLint success; + glGetProgramiv(program, GL_LINK_STATUS, &success); + if (!success) { + checkProgramLink(program); + glDeleteProgram(program); + return 0; + } + return program; +} + int main(int argc, char **argv) { (void)argc; (void)argv; @@ -135,7 +202,7 @@ int main(int argc, char **argv) { #endif // ウィンドウを作成 - GLFWwindow *window = glfwCreateWindow(800, 600, "OpenGLシェーダーテスト", NULL, NULL); + GLFWwindow *window = glfwCreateWindow(800, 600, "GLSLシェーダープレイグラウンド", NULL, NULL); if (!window) { puts("GLFWウィンドウの作成に失敗しました"); glfwTerminate(); @@ -151,50 +218,22 @@ int main(int argc, char **argv) { return -1; } - // ビューポートを設定 - glViewport(0, 0, 800, 600); + // ビューポートを設定(Retina等では実際のピクセル数がウィンドウの大きさと違う) + glfwGetFramebufferSize(window, &gWidth, &gHeight); + glViewport(0, 0, gWidth, gHeight); glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); - // シェーダーファイルを読み込む -#ifdef __APPLE__ - char *vertexShaderSource = readShaderFile("shader/vertex.mac.glsl"); - char *fragmentShaderSource = readShaderFile("shader/fragment.mac.glsl"); -#else - char *vertexShaderSource = readShaderFile("shader/vertex.glsl"); - char *fragmentShaderSource = readShaderFile("shader/fragment.glsl"); -#endif - if (!vertexShaderSource || !fragmentShaderSource) { - puts("シェーダーファイルの読み込みに失敗しました"); - if (vertexShaderSource) free(vertexShaderSource); - if (fragmentShaderSource) free(fragmentShaderSource); + // シェーダーを読み込む + const char *vertexPath = "shader/vertex.glsl"; + const char *fragmentPath = "shader/playground.glsl"; + GLuint shaderProgram = buildProgram(vertexPath, fragmentPath); + if (!shaderProgram) { + puts("シェーダーの読み込みに失敗しました"); glfwDestroyWindow(window); glfwTerminate(); return -1; } - // シェーダーをコンパイル - GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(vertexShader, 1, (const char **)&vertexShaderSource, NULL); - glCompileShader(vertexShader); - checkShaderCompile(vertexShader); - - GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(fragmentShader, 1, (const char **)&fragmentShaderSource, NULL); - glCompileShader(fragmentShader); - checkShaderCompile(fragmentShader); - - // シェーダープログラムをリンク - GLuint shaderProgram = glCreateProgram(); - glAttachShader(shaderProgram, vertexShader); - glAttachShader(shaderProgram, fragmentShader); - glLinkProgram(shaderProgram); - checkProgramLink(shaderProgram); - - glDeleteShader(vertexShader); - glDeleteShader(fragmentShader); - free(vertexShaderSource); - free(fragmentShaderSource); - // 頂点データ(フルスクリーンクアッド)を設定 float vertices[] = { -1.0f, -1.0f, 0.0f, 0.0f, // 左下(位置、UV) @@ -222,8 +261,27 @@ int main(int argc, char **argv) { glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)offset); glEnableVertexAttribArray(1); + int reloadWasDown = 0; + // メインレンダリングループ while (!glfwWindowShouldClose(window)) { + // ESCキーで終了 + if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, 1); + + // Rキーでシェーダーを再読み込み(失敗したら古いシェーダーのまま) + int reloadDown = glfwGetKey(window, GLFW_KEY_R) == GLFW_PRESS; + if (reloadDown && !reloadWasDown) { + GLuint newProgram = buildProgram(vertexPath, fragmentPath); + if (newProgram) { + glDeleteProgram(shaderProgram); + shaderProgram = newProgram; + puts("シェーダーを再読み込みしました"); + } else { + puts("再読み込みに失敗しました。前のシェーダーを使い続けます"); + } + } + reloadWasDown = reloadDown; + // 画面をクリア glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); @@ -231,33 +289,18 @@ int main(int argc, char **argv) { // シェーダープログラムを使用 glUseProgram(shaderProgram); - // 時間に基づいて色をアニメーション - float time = (float)glfwGetTime(); - float speed = 1.0f; // アニメーションの速度 - float phase = 2.0f * 3.14159f / 3.0f; // // 120度の位相差(3色用) - - // uColorA: 赤 -> 緑 -> 青 - Vector3 colorA; - colorA.r = sin(time * speed + 0.0f) * 0.5f + 0.5f; // 0.0 to 1.0 - colorA.g = sin(time * speed + phase) * 0.5f + 0.5f; - colorA.b = sin(time * speed + 2.0f * phase) * 0.5f + 0.5f; - - // uColorB: 緑 -> 青 -> 赤 - Vector3 colorB; - colorB.r = sin(time * speed + 2.0f * phase) * 0.5f + 0.5f; - colorB.g = sin(time * speed + 0.0f) * 0.5f + 0.5f; - colorB.b = sin(time * speed + phase) * 0.5f + 0.5f; - - // uColorC: 青 -> 赤 -> 緑 - Vector3 colorC; - colorC.r = sin(time * speed + phase) * 0.5f + 0.5f; - colorC.g = sin(time * speed + 2.0f * phase) * 0.5f + 0.5f; - colorC.b = sin(time * speed + 0.0f) * 0.5f + 0.5f; + // マウスの位置(ピクセル、左下が原点になる様にyを反転) + double mouseX, mouseY; + glfwGetCursorPos(window, &mouseX, &mouseY); + int windowW, windowH; + glfwGetWindowSize(window, &windowW, &windowH); + float scale = windowW > 0 ? (float)gWidth / (float)windowW : 1.0f; // ユニフォームを設定 - glUniform3f(glGetUniformLocation(shaderProgram, "uColorA"), colorA.r, colorA.g, colorA.b); // 赤 - glUniform3f(glGetUniformLocation(shaderProgram, "uColorB"), colorB.r, colorB.g, colorB.b); // 緑 - glUniform3f(glGetUniformLocation(shaderProgram, "uColorC"), colorC.r, colorC.g, colorC.b); // 青 + glUniform1f(glGetUniformLocation(shaderProgram, "uTime"), (float)glfwGetTime()); + glUniform2f(glGetUniformLocation(shaderProgram, "uResolution"), (float)gWidth, (float)gHeight); + glUniform2f(glGetUniformLocation(shaderProgram, "uMouse"), + (float)mouseX * scale, (float)(windowH - mouseY) * scale); // クアッドを描画 glBindVertexArray(VAO); diff --git a/shader/playground.glsl b/shader/playground.glsl new file mode 100644 index 0000000..8ec93da --- /dev/null +++ b/shader/playground.glsl @@ -0,0 +1,22 @@ +#version 450 core + +in vec2 vUv; +out vec4 out_color; + +uniform float uTime; // 起動してからの秒数 +uniform vec2 uResolution; // 画面の大きさ(ピクセル) +uniform vec2 uMouse; // マウスの位置(ピクセル、左下が原点) + +void main() { + // ピクセル座標を0〜1に正規化 + vec2 uv = gl_FragCoord.xy / uResolution; + + // 時間で動くグラデーション + vec3 color = 0.5 + 0.5 * cos(uTime + uv.xyx + vec3(0.0, 2.0, 4.0)); + + // マウスの周りに白い円 + float d = length(gl_FragCoord.xy - uMouse); + color = mix(color, vec3(1.0), 1.0 - smoothstep(18.0, 20.0, d)); + + out_color = vec4(color, 1.0); +}