Search code examples
opengl-esglsl

Implementing Pencil Brush Texture in OpenGL for Drawing Application


enter image description here

I am working on a drawing application in OpenGL and would like to simulate a pencil brush similar to what applications like Krita and Procreate use. However, I'm facing challenges with getting the texture right. I'm currently drawing a square texture at each point using both a pencil texture and a noise texture. Both of these textures are grayscale.

Here is a snippet of my code:

// [Code snippet]
for (td in touchDataManager.get()) {
    val startTime = System.nanoTime()
    brush.color = color
    brush.updateBrush(td)
    val endTime = System.nanoTime()
    val newTime = (endTime - startTime) / 1000000f
    timeProfilerHelper.add(newTime)

    square.draw(
        brushMVPMatrix,
        textureId,
        noiseTextureId,
        floatArrayOf(
            brush.color[0],
            brush.color[1],
            brush.color[2],
            min(brush.color[3] * td.normalizedSize, 1f).also {
            })
    )
    index++
}

The square.draw function is responsible for rendering the brush stroke using the specified textures and parameters.

Here's the relevant part of the draw function:

// [Code snippet]
fun draw(mvpMatrix: FloatArray?, textureId: Int, noiseTextureId : Int , squareColor: FloatArray) {
    // [Code to set up OpenGL program and uniforms]
    // ...

    GLES30.glDrawArrays(GLES30.GL_TRIANGLE_FAN, 0, 4)

    GLES30.glDisableVertexAttribArray(positionHandle)
    GLES30.glDisableVertexAttribArray(texCoordHandle)
}

Additionally, here is the fragment shader:

// [Fragment shader code]
precision mediump float;
uniform sampler2D uTexture;
uniform sampler2D uNoiseTexture;
varying vec2 vTexCoord;
uniform vec4 vColor;
uniform vec2 noiseTextureSize;
uniform float noiseScale;
uniform vec2 noiseOffset;
void main() {
    // [Shader code]
    // ...
}

I'm looking to implement a pencil brush effect similar to what you find in drawing applications. However, the results are not as expected, and I'm struggling with achieving a realistic pencil texture.

Possible Solution:

I have considered using stencil buffers but I'm not sure if that's the right approach. Another idea I've had is to draw a hidden noise texture and use the brush texture as a stencil or mask to reveal the noise texture behind it. However, I'm uncertain about how to proceed and would appreciate guidance on implementing the pencil brush effect in OpenGL.

I would like to know the drawing techniques and shader adjustments that can help me achieve a more realistic pencil texture similar to what's seen in professional drawing applications like Krita and Procreate. Any insights, code samples, or suggestions would be greatly appreciated. Thank you!


Solution

  • Yes you can use stencil buffer to get what you want. Here is simple implementation of stencil buffer. It is C++ but you will get the Idea.

    #include <GL/glew.h>
    #include <GLFW/glfw3.h>
    
    void renderScene() {
        // Enable the stencil test
        glEnable(GL_STENCIL_TEST);
        
        // Clear the stencil buffer to zero at the beginning of each frame
        glClearStencil(0);
        glClear(GL_STENCIL_BUFFER_BIT);
        
        // Set the stencil test function to always pass and update the stencil buffer
        glStencilFunc(GL_ALWAYS, 1, 0xFF);
        glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
        
        // Render the areas where you want the pencil brush effect
        // Bind your pencil brush texture and render a quad or other shapes here
        
        // Disable writing to the stencil buffer and set the stencil test function to only render where the stencil buffer is not zero
        glStencilFunc(GL_NOTEQUAL, 0, 0xFF);
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
        
        // Render the scene (background, objects, etc.) as you normally would
        
        // Disable the stencil test
        glDisable(GL_STENCIL_TEST);
        
        // Swap buffers and update the screen
        glfwSwapBuffers(window);
    }
    
    int main() {
        // Initialize GLFW and create a window
        if (!glfwInit()) {
            return -1;
        }
        
        // Set up your OpenGL context and window here
        
        // Load your pencil brush texture
        
        while (!glfwWindowShouldClose(window)) {
            // Process user input and update the scene
        
            // Render the scene with the pencil brush effect
            renderScene();
        
            // Poll for and process events
            glfwPollEvents();
        }
        
        // Cleanup and exit
        glfwTerminate();
        
        return 0;
    }