#include "SimpleMotionRenderer.h" #include "GLExtensions.h" #include #include #include namespace { constexpr GLuint kInputTextureUnit = 0; const char* kTextureVertexShader = R"GLSL( #version 430 core out vec2 vTexCoord; void main() { vec2 positions[3] = vec2[3]( vec2(-1.0, -1.0), vec2( 3.0, -1.0), vec2(-1.0, 3.0)); vec2 texCoords[3] = vec2[3]( vec2(0.0, 0.0), vec2(2.0, 0.0), vec2(0.0, 2.0)); gl_Position = vec4(positions[gl_VertexID], 0.0, 1.0); vTexCoord = texCoords[gl_VertexID]; } )GLSL"; const char* kTextureFragmentShader = R"GLSL( #version 430 core layout(binding = 0) uniform sampler2D uInputTexture; in vec2 vTexCoord; out vec4 fragColor; void main() { fragColor = texture(uInputTexture, clamp(vTexCoord, vec2(0.0), vec2(1.0))); } )GLSL"; const char* kPatternFragmentShader = R"GLSL( #version 430 core uniform vec2 uResolution; uniform float uFrameIndex; in vec2 vTexCoord; out vec4 fragColor; vec3 hexColor(float r, float g, float b) { return vec3(r, g, b) / 255.0; } vec3 smpteTop(float x) { if (x < 240.0) return hexColor(102.0, 102.0, 102.0); if (x < 445.0) return hexColor(191.0, 191.0, 191.0); if (x < 651.0) return hexColor(191.0, 191.0, 0.0); if (x < 857.0) return hexColor(0.0, 191.0, 191.0); if (x < 1063.0) return hexColor(0.0, 191.0, 0.0); if (x < 1269.0) return hexColor(191.0, 0.0, 191.0); if (x < 1475.0) return hexColor(191.0, 0.0, 0.0); if (x < 1680.0) return hexColor(0.0, 0.0, 191.0); return hexColor(102.0, 102.0, 102.0); } vec3 smpteMiddleA(float x) { if (x < 240.0) return hexColor(0.0, 255.0, 255.0); if (x < 445.0) return hexColor(0.0, 63.0, 105.0); if (x < 1680.0) return hexColor(191.0, 191.0, 191.0); return hexColor(0.0, 0.0, 255.0); } vec3 smpteMiddleB(float x) { if (x < 240.0) return hexColor(255.0, 255.0, 0.0); if (x < 445.0) return hexColor(65.0, 0.0, 119.0); if (x < 1475.0) { float ramp = clamp((x - 445.0) / (1475.0 - 445.0), 0.0, 1.0); return vec3(ramp); } if (x < 1680.0) return vec3(1.0); return hexColor(255.0, 0.0, 0.0); } vec3 smpteBottom(float x) { if (x < 240.0) return hexColor(38.0, 38.0, 38.0); if (x < 549.0) return vec3(0.0); if (x < 960.0) return vec3(1.0); if (x < 1268.0) return vec3(0.0); if (x < 1337.0) return hexColor(5.0, 5.0, 5.0); if (x < 1405.0) return vec3(0.0); if (x < 1474.0) return hexColor(10.0, 10.0, 10.0); if (x < 1680.0) return vec3(0.0); return hexColor(38.0, 38.0, 38.0); } vec3 smpteColor(vec2 uv) { vec2 pixel = vec2(clamp(uv.x, 0.0, 1.0), 1.0 - clamp(uv.y, 0.0, 1.0)) * vec2(1920.0, 1080.0); if (pixel.y < 630.0) return smpteTop(pixel.x); if (pixel.y < 720.0) return smpteMiddleA(pixel.x); if (pixel.y < 810.0) return smpteMiddleB(pixel.x); return smpteBottom(pixel.x); } void main() { vec2 uv = clamp(vTexCoord, vec2(0.0), vec2(1.0)); vec3 color = smpteColor(uv); float t = uFrameIndex / 60.0; vec2 cubeSize = vec2(0.16, 0.20); vec2 cubeMin = vec2( (0.5 + 0.5 * sin(t * 1.7)) * (1.0 - cubeSize.x), (0.5 + 0.5 * sin(t * 1.1 + 0.8)) * (1.0 - cubeSize.y)); vec2 cubeMax = cubeMin + cubeSize; bool insideCube = uv.x >= cubeMin.x && uv.x <= cubeMax.x && uv.y >= cubeMin.y && uv.y <= cubeMax.y; if (insideCube) { vec2 local = (uv - cubeMin) / cubeSize; vec3 cubeColor = vec3(1.0, 0.74 + 0.18 * sin(t * 2.1), 0.08); float edge = step(local.x, 0.04) + step(local.y, 0.04) + step(0.96, local.x) + step(0.96, local.y); color = edge > 0.0 ? vec3(1.0) : cubeColor; } fragColor = vec4(color, 1.0); } )GLSL"; } bool SimpleMotionRenderer::InitializeGl(unsigned width, unsigned height) { mWidth = width; mHeight = height; return mWidth > 0 && mHeight > 0; } void SimpleMotionRenderer::RenderFrame(uint64_t frameIndex) { if (!EnsurePatternProgram()) { glViewport(0, 0, static_cast(mWidth), static_cast(mHeight)); glDisable(GL_SCISSOR_TEST); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); return; } glViewport(0, 0, static_cast(mWidth), static_cast(mHeight)); glDisable(GL_SCISSOR_TEST); glDisable(GL_DEPTH_TEST); glDisable(GL_BLEND); glUseProgram(mPatternProgram); const GLint resolutionLocation = glGetUniformLocation(mPatternProgram, "uResolution"); if (resolutionLocation >= 0) glUniform2f(resolutionLocation, static_cast(mWidth), static_cast(mHeight)); const GLint frameIndexLocation = glGetUniformLocation(mPatternProgram, "uFrameIndex"); if (frameIndexLocation >= 0) glUniform1f(frameIndexLocation, static_cast(frameIndex)); glBindVertexArray(mPatternVertexArray); glDrawArrays(GL_TRIANGLES, 0, 3); glBindVertexArray(0); glUseProgram(0); } void SimpleMotionRenderer::RenderTexture(GLuint texture) { if (texture == 0 || !EnsureTextureProgram()) { RenderFrame(0); return; } glViewport(0, 0, static_cast(mWidth), static_cast(mHeight)); glDisable(GL_SCISSOR_TEST); glDisable(GL_DEPTH_TEST); glDisable(GL_BLEND); glActiveTexture(GL_TEXTURE0 + kInputTextureUnit); glBindTexture(GL_TEXTURE_2D, texture); glUseProgram(mTextureProgram); glBindVertexArray(mTextureVertexArray); glDrawArrays(GL_TRIANGLES, 0, 3); glBindVertexArray(0); glUseProgram(0); glBindTexture(GL_TEXTURE_2D, 0); glActiveTexture(GL_TEXTURE0); } void SimpleMotionRenderer::ShutdownGl() { DestroyPatternProgram(); DestroyTextureProgram(); mWidth = 0; mHeight = 0; } bool SimpleMotionRenderer::EnsurePatternProgram() { if (mPatternProgram != 0) return true; if (!CompileShader(GL_VERTEX_SHADER, kTextureVertexShader, mPatternVertexShader)) return false; if (!CompileShader(GL_FRAGMENT_SHADER, kPatternFragmentShader, mPatternFragmentShader)) { DestroyPatternProgram(); return false; } mPatternProgram = glCreateProgram(); glAttachShader(mPatternProgram, mPatternVertexShader); glAttachShader(mPatternProgram, mPatternFragmentShader); glLinkProgram(mPatternProgram); GLint linkResult = GL_FALSE; glGetProgramiv(mPatternProgram, GL_LINK_STATUS, &linkResult); if (linkResult == GL_FALSE) { DestroyPatternProgram(); return false; } glGenVertexArrays(1, &mPatternVertexArray); return mPatternVertexArray != 0; } bool SimpleMotionRenderer::EnsureTextureProgram() { if (mTextureProgram != 0) return true; if (!CompileShader(GL_VERTEX_SHADER, kTextureVertexShader, mTextureVertexShader)) return false; if (!CompileShader(GL_FRAGMENT_SHADER, kTextureFragmentShader, mTextureFragmentShader)) { DestroyTextureProgram(); return false; } mTextureProgram = glCreateProgram(); glAttachShader(mTextureProgram, mTextureVertexShader); glAttachShader(mTextureProgram, mTextureFragmentShader); glLinkProgram(mTextureProgram); GLint linkResult = GL_FALSE; glGetProgramiv(mTextureProgram, GL_LINK_STATUS, &linkResult); if (linkResult == GL_FALSE) { DestroyTextureProgram(); return false; } glUseProgram(mTextureProgram); const GLint inputLocation = glGetUniformLocation(mTextureProgram, "uInputTexture"); if (inputLocation >= 0) glUniform1i(inputLocation, static_cast(kInputTextureUnit)); glUseProgram(0); glGenVertexArrays(1, &mTextureVertexArray); return mTextureVertexArray != 0; } void SimpleMotionRenderer::DestroyTextureProgram() { if (mTextureProgram != 0) glDeleteProgram(mTextureProgram); if (mTextureVertexShader != 0) glDeleteShader(mTextureVertexShader); if (mTextureFragmentShader != 0) glDeleteShader(mTextureFragmentShader); if (mTextureVertexArray != 0) glDeleteVertexArrays(1, &mTextureVertexArray); mTextureProgram = 0; mTextureVertexShader = 0; mTextureFragmentShader = 0; mTextureVertexArray = 0; } void SimpleMotionRenderer::DestroyPatternProgram() { if (mPatternProgram != 0) glDeleteProgram(mPatternProgram); if (mPatternVertexShader != 0) glDeleteShader(mPatternVertexShader); if (mPatternFragmentShader != 0) glDeleteShader(mPatternFragmentShader); if (mPatternVertexArray != 0) glDeleteVertexArrays(1, &mPatternVertexArray); mPatternProgram = 0; mPatternVertexShader = 0; mPatternFragmentShader = 0; mPatternVertexArray = 0; } bool SimpleMotionRenderer::CompileShader(GLenum shaderType, const char* source, GLuint& shader) { shader = glCreateShader(shaderType); glShaderSource(shader, 1, &source, nullptr); glCompileShader(shader); GLint compileResult = GL_FALSE; glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult); if (compileResult != GL_FALSE) return true; glDeleteShader(shader); shader = 0; return false; }