22
33BlurRenderer::BlurRenderer (int width, int height)
44 : eglHelper_(width, height),
5- quadVBO_(0 ), shaderProgram_ (0 ),
5+ quadVBO_(0 ), horizontalShaderProgram_( 0 ), verticalShaderProgram_ (0 ),
66 attrPos_(-1 ), attrTexCoord_(-1 ),
7- uniformTexture_(-1 ), uniformRadius_(-1 ), uniformTextureSize_(-1 ),
8- framebuffer_ (0 ), fboTexture_ (0 ), initialized_(false ) {}
7+ uniformTexture_(-1 ), uniformRadius_(-1 ), uniformTextureSize_(-1 ), uniformDirection_(- 1 ),
8+ framebuffer1_ (0 ), framebuffer2_( 0 ), fboTexture1_( 0 ), fboTexture2_ (0 ), initialized_(false ) {}
99
1010void BlurRenderer::initialize () {
1111 if (initialized_) return ;
@@ -14,7 +14,7 @@ void BlurRenderer::initialize() {
1414
1515 compileShaders ();
1616 setupFullscreenQuad ();
17- setupFramebuffer ();
17+ setupFramebuffers ();
1818
1919 initialized_ = true ;
2020}
@@ -32,6 +32,8 @@ GLuint BlurRenderer::uploadBitmapAsTexture(unsigned char* pixels, int width, int
3232
3333 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
3434 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
35+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
36+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3537
3638 return textureId;
3739}
@@ -40,36 +42,94 @@ void BlurRenderer::render(GLuint textureId, int width, int height, float radius)
4042 initialize ();
4143 eglHelper_.makeCurrent ();
4244
43- glUseProgram (shaderProgram_);
44- glBindFramebuffer (GL_FRAMEBUFFER, framebuffer_);
45+ if (radius <= 0 .5f ) {
46+ // No blur needed, just render directly
47+ renderDirect (textureId, width, height);
48+ return ;
49+ }
50+
51+ // Resize framebuffer textures if needed
52+ resizeFramebuffers (width, height);
53+
54+ // First pass: Horizontal blur
55+ renderHorizontalPass (textureId, width, height, radius);
56+
57+ // Second pass: Vertical blur
58+ renderVerticalPass (width, height, radius);
59+ }
60+
61+ void BlurRenderer::renderHorizontalPass (GLuint textureId, int width, int height, float radius) {
62+ glUseProgram (horizontalShaderProgram_);
63+ glBindFramebuffer (GL_FRAMEBUFFER, framebuffer1_);
4564 glViewport (0 , 0 , width, height);
4665
47- glUniform1f (uniformRadius_, radius);
48- glUniform2f (uniformTextureSize_, static_cast <float >(width), static_cast <float >(height));
66+ // Set uniforms
67+ glUniform1f (glGetUniformLocation (horizontalShaderProgram_, " uRadius" ), radius);
68+ glUniform2f (glGetUniformLocation (horizontalShaderProgram_, " uTextureSize" ),
69+ static_cast <float >(width), static_cast <float >(height));
4970
71+ // Bind input texture
5072 glActiveTexture (GL_TEXTURE0);
5173 glBindTexture (GL_TEXTURE_2D, textureId);
52- glUniform1i (uniformTexture_ , 0 );
74+ glUniform1i (glGetUniformLocation (horizontalShaderProgram_, " uTexture " ) , 0 );
5375
54- glBindBuffer (GL_ARRAY_BUFFER, quadVBO_);
76+ drawQuad (horizontalShaderProgram_);
77+
78+ glBindFramebuffer (GL_FRAMEBUFFER, 0 );
79+ }
5580
56- glEnableVertexAttribArray (attrPos_);
57- glVertexAttribPointer (attrPos_, 2 , GL_FLOAT, GL_FALSE, 4 * sizeof (float ), (void *)0 );
81+ void BlurRenderer::renderVerticalPass (int width, int height, float radius) {
82+ glUseProgram (verticalShaderProgram_);
83+ glBindFramebuffer (GL_FRAMEBUFFER, framebuffer2_);
84+ glViewport (0 , 0 , width, height);
5885
59- glEnableVertexAttribArray (attrTexCoord_);
60- glVertexAttribPointer (attrTexCoord_, 2 , GL_FLOAT, GL_FALSE, 4 * sizeof (float ), (void *)(2 * sizeof (float )));
86+ // Set uniforms
87+ glUniform1f (glGetUniformLocation (verticalShaderProgram_, " uRadius" ), radius);
88+ glUniform2f (glGetUniformLocation (verticalShaderProgram_, " uTextureSize" ),
89+ static_cast <float >(width), static_cast <float >(height));
6190
62- glDrawArrays (GL_TRIANGLE_STRIP, 0 , 4 );
91+ // Bind intermediate texture from horizontal pass
92+ glActiveTexture (GL_TEXTURE0);
93+ glBindTexture (GL_TEXTURE_2D, fboTexture1_);
94+ glUniform1i (glGetUniformLocation (verticalShaderProgram_, " uTexture" ), 0 );
6395
64- glDisableVertexAttribArray (attrPos_);
65- glDisableVertexAttribArray (attrTexCoord_);
96+ drawQuad (verticalShaderProgram_);
6697
6798 glBindFramebuffer (GL_FRAMEBUFFER, 0 );
6899}
69100
101+ void BlurRenderer::renderDirect (GLuint textureId, int width, int height) {
102+ // Simple pass-through for no blur
103+ glBindFramebuffer (GL_FRAMEBUFFER, framebuffer2_);
104+ glViewport (0 , 0 , width, height);
105+
106+ // Clear and copy texture directly
107+ glClear (GL_COLOR_BUFFER_BIT);
108+ // You could implement a simple copy shader here, or just use the vertical shader with radius 0
109+ glBindFramebuffer (GL_FRAMEBUFFER, 0 );
110+ }
111+
112+ void BlurRenderer::drawQuad (GLuint shaderProgram) {
113+ glBindBuffer (GL_ARRAY_BUFFER, quadVBO_);
114+
115+ GLint posLoc = glGetAttribLocation (shaderProgram, " aPosition" );
116+ GLint texLoc = glGetAttribLocation (shaderProgram, " aTexCoord" );
117+
118+ glEnableVertexAttribArray (posLoc);
119+ glVertexAttribPointer (posLoc, 2 , GL_FLOAT, GL_FALSE, 4 * sizeof (float ), (void *)0 );
120+
121+ glEnableVertexAttribArray (texLoc);
122+ glVertexAttribPointer (texLoc, 2 , GL_FLOAT, GL_FALSE, 4 * sizeof (float ), (void *)(2 * sizeof (float )));
123+
124+ glDrawArrays (GL_TRIANGLE_STRIP, 0 , 4 );
125+
126+ glDisableVertexAttribArray (posLoc);
127+ glDisableVertexAttribArray (texLoc);
128+ }
129+
70130void BlurRenderer::readFBO (unsigned char * pixels, int width, int height) {
71131 eglHelper_.makeCurrent ();
72- glBindFramebuffer (GL_FRAMEBUFFER, framebuffer_);
132+ glBindFramebuffer (GL_FRAMEBUFFER, framebuffer2_); // Read from final output
73133 glReadPixels (0 , 0 , width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
74134 glBindFramebuffer (GL_FRAMEBUFFER, 0 );
75135}
@@ -88,15 +148,45 @@ void BlurRenderer::setupFullscreenQuad() {
88148 glBindBuffer (GL_ARRAY_BUFFER, 0 );
89149}
90150
91- void BlurRenderer::setupFramebuffer () {
92- glGenFramebuffers (1 , &framebuffer_);
93- glBindFramebuffer (GL_FRAMEBUFFER, framebuffer_);
151+ void BlurRenderer::setupFramebuffers () {
152+ // Create two framebuffers for ping-pong rendering
153+ glGenFramebuffers (1 , &framebuffer1_);
154+ glGenFramebuffers (1 , &framebuffer2_);
94155
95- glGenTextures (1 , &fboTexture_);
96- glBindTexture (GL_TEXTURE_2D, fboTexture_);
97- glTexImage2D (GL_TEXTURE_2D, 0 , GL_RGBA, 4096 , 4096 , 0 , GL_RGBA, GL_UNSIGNED_BYTE, nullptr );
156+ glGenTextures (1 , &fboTexture1_);
157+ glGenTextures (1 , &fboTexture2_);
158+
159+ currentWidth_ = 0 ;
160+ currentHeight_ = 0 ;
161+ }
98162
99- glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboTexture_, 0 );
163+ void BlurRenderer::resizeFramebuffers (int width, int height) {
164+ if (currentWidth_ == width && currentHeight_ == height) {
165+ return ; // No resize needed
166+ }
167+
168+ currentWidth_ = width;
169+ currentHeight_ = height;
170+
171+ // Setup first framebuffer
172+ glBindFramebuffer (GL_FRAMEBUFFER, framebuffer1_);
173+ glBindTexture (GL_TEXTURE_2D, fboTexture1_);
174+ glTexImage2D (GL_TEXTURE_2D, 0 , GL_RGBA, width, height, 0 , GL_RGBA, GL_UNSIGNED_BYTE, nullptr );
175+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
176+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
177+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
178+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
179+ glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboTexture1_, 0 );
180+
181+ // Setup second framebuffer
182+ glBindFramebuffer (GL_FRAMEBUFFER, framebuffer2_);
183+ glBindTexture (GL_TEXTURE_2D, fboTexture2_);
184+ glTexImage2D (GL_TEXTURE_2D, 0 , GL_RGBA, width, height, 0 , GL_RGBA, GL_UNSIGNED_BYTE, nullptr );
185+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
186+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
187+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
188+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
189+ glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboTexture2_, 0 );
100190
101191 glBindFramebuffer (GL_FRAMEBUFFER, 0 );
102192}
@@ -105,6 +195,18 @@ GLuint BlurRenderer::compileShader(GLenum type, const char* source) {
105195 GLuint shader = glCreateShader (type);
106196 glShaderSource (shader, 1 , &source, nullptr );
107197 glCompileShader (shader);
198+
199+ // Check compilation status
200+ GLint success;
201+ glGetShaderiv (shader, GL_COMPILE_STATUS, &success);
202+ if (!success) {
203+ GLchar infoLog[512 ];
204+ glGetShaderInfoLog (shader, 512 , nullptr , infoLog);
205+ // Log error here if needed
206+ glDeleteShader (shader);
207+ return 0 ;
208+ }
209+
108210 return shader;
109211}
110212
@@ -119,7 +221,8 @@ void BlurRenderer::compileShaders() {
119221 }
120222 )" ;
121223
122- const char * fragmentShaderSrc = R"(
224+ // Optimized horizontal blur fragment shader
225+ const char * horizontalFragmentShaderSrc = R"(
123226 precision mediump float;
124227 varying vec2 vTexCoord;
125228 uniform sampler2D uTexture;
@@ -129,51 +232,96 @@ void BlurRenderer::compileShaders() {
129232 void main() {
130233 vec2 texelSize = 1.0 / uTextureSize;
131234 vec4 color = vec4(0.0);
132- float totalWeight = 0.0;
133- float pixelRadius = uRadius;
134- if (pixelRadius <= 0.5) {
235+
236+ if (uRadius <= 0.5) {
135237 gl_FragColor = texture2D(uTexture, vTexCoord);
136238 return;
137239 }
138- float sigma = pixelRadius / 2.0;
139- int iRadius = int(ceil(pixelRadius));
140240
241+ float sigma = uRadius / 2.0;
242+ float twoSigmaSq = 2.0 * sigma * sigma;
243+ float totalWeight = 0.0;
244+ int iRadius = int(ceil(uRadius));
245+
246+ // Sample along horizontal axis only
141247 for (int x = -iRadius; x <= iRadius; ++x) {
142- for (int y = -iRadius; y <= iRadius; ++y) {
143- float dist = sqrt(float(x * x + y * y));
144- if (dist <= pixelRadius) {
145- vec2 offset = vec2(float(x), float(y)) * texelSize;
146- vec2 sampleCoord = clamp(vTexCoord + offset, vec2(0.0), vec2(1.0));
147- float weight = exp(-(dist * dist) / (2.0 * sigma * sigma));
148- color += texture2D(uTexture, sampleCoord) * weight;
149- totalWeight += weight;
150- }
248+ float distance = abs(float(x));
249+ if (distance <= uRadius) {
250+ vec2 sampleCoord = vTexCoord + vec2(float(x) * texelSize.x, 0.0);
251+ sampleCoord = clamp(sampleCoord, vec2(0.0), vec2(1.0));
252+
253+ float weight = exp(-(distance * distance) / twoSigmaSq);
254+ color += texture2D(uTexture, sampleCoord) * weight;
255+ totalWeight += weight;
151256 }
152257 }
153258
154- if (totalWeight > 0.0) {
155- color /= totalWeight;
259+ gl_FragColor = color / totalWeight;
260+ }
261+ )" ;
262+
263+ // Optimized vertical blur fragment shader
264+ const char * verticalFragmentShaderSrc = R"(
265+ precision mediump float;
266+ varying vec2 vTexCoord;
267+ uniform sampler2D uTexture;
268+ uniform float uRadius;
269+ uniform vec2 uTextureSize;
270+
271+ void main() {
272+ vec2 texelSize = 1.0 / uTextureSize;
273+ vec4 color = vec4(0.0);
274+
275+ if (uRadius <= 0.5) {
276+ gl_FragColor = texture2D(uTexture, vTexCoord);
277+ return;
278+ }
279+
280+ float sigma = uRadius / 2.0;
281+ float twoSigmaSq = 2.0 * sigma * sigma;
282+ float totalWeight = 0.0;
283+ int iRadius = int(ceil(uRadius));
284+
285+ // Sample along vertical axis only
286+ for (int y = -iRadius; y <= iRadius; ++y) {
287+ float distance = abs(float(y));
288+ if (distance <= uRadius) {
289+ vec2 sampleCoord = vTexCoord + vec2(0.0, float(y) * texelSize.y);
290+ sampleCoord = clamp(sampleCoord, vec2(0.0), vec2(1.0));
291+
292+ float weight = exp(-(distance * distance) / twoSigmaSq);
293+ color += texture2D(uTexture, sampleCoord) * weight;
294+ totalWeight += weight;
295+ }
156296 }
157297
158- gl_FragColor = color;
298+ gl_FragColor = color / totalWeight ;
159299 }
160300 )" ;
161301
302+ // Compile vertex shader (shared)
162303 GLuint vertexShader = compileShader (GL_VERTEX_SHADER, vertexShaderSrc);
163- GLuint fragmentShader = compileShader (GL_FRAGMENT_SHADER, fragmentShaderSrc);
164304
165- shaderProgram_ = glCreateProgram ();
166- glAttachShader (shaderProgram_, vertexShader);
167- glAttachShader (shaderProgram_, fragmentShader);
168- glLinkProgram (shaderProgram_);
305+ // Compile horizontal fragment shader
306+ GLuint horizontalFragmentShader = compileShader (GL_FRAGMENT_SHADER, horizontalFragmentShaderSrc);
307+
308+ // Compile vertical fragment shader
309+ GLuint verticalFragmentShader = compileShader (GL_FRAGMENT_SHADER, verticalFragmentShaderSrc);
169310
170- attrPos_ = glGetAttribLocation (shaderProgram_, " aPosition" );
171- attrTexCoord_ = glGetAttribLocation (shaderProgram_, " aTexCoord" );
311+ // Create horizontal blur program
312+ horizontalShaderProgram_ = glCreateProgram ();
313+ glAttachShader (horizontalShaderProgram_, vertexShader);
314+ glAttachShader (horizontalShaderProgram_, horizontalFragmentShader);
315+ glLinkProgram (horizontalShaderProgram_);
172316
173- uniformTexture_ = glGetUniformLocation (shaderProgram_, " uTexture" );
174- uniformRadius_ = glGetUniformLocation (shaderProgram_, " uRadius" );
175- uniformTextureSize_ = glGetUniformLocation (shaderProgram_, " uTextureSize" );
317+ // Create vertical blur program
318+ verticalShaderProgram_ = glCreateProgram ();
319+ glAttachShader (verticalShaderProgram_, vertexShader);
320+ glAttachShader (verticalShaderProgram_, verticalFragmentShader);
321+ glLinkProgram (verticalShaderProgram_);
176322
323+ // Clean up shaders
177324 glDeleteShader (vertexShader);
178- glDeleteShader (fragmentShader);
179- }
325+ glDeleteShader (horizontalFragmentShader);
326+ glDeleteShader (verticalFragmentShader);
327+ }
0 commit comments