edit stroke position and stroke color of a PShape using shader

edited June 2015 in GLSL / Shaders

Hello !

I'm using a vertex shader with a Pshape to do some Vertex Displacement Mapping. It works fine with the shape but how can I access and edit its stroke (in red in the picture) ?

Do you have some examples ?

objv-1433599760740

Tagged:

Answers

  • I have found a solution. I'm using two shaders: one for the surfaces, another for the strokes but it is not optimized: I do the same calculations two times. Do you know another way to proceed ?

    objv-1433688626209

  • edited September 2015 Answer ✓

    If "stretched" lines are not a problem, you could use a simple wireframe texture, or fragment shader behaving like on:

    textureWireframe

    float angle = 0.0;
    float offset;
    
    void setup() {
        size(800, 800, P3D);
        noStroke();
        textureMode(NORMAL);
        initShader();
    }
    
    void draw() {
        background(0);
        camera(sin(angle += 0.0005) * 800, -800, cos(angle) * 800, 0, 500, 0, 0, 1, 0);
        renderLandscape();
    }
    
    public void initShader() {
        String[] vertSource = {
            "uniform mat4 transform;",
    
            "attribute vec4 vertex;",
            "attribute vec4 color;",
            "attribute vec2 texCoord;",
    
            "varying vec4 vertColor;",
            "varying vec2 vertTexCoord;",
    
            "void main() {",
                "vertColor = color;",
                "vertTexCoord = texCoord;",
    
                "gl_Position = transform * vertex;",
            "}"
        };
        String[] fragSource = {
            "uniform sampler2D texture;",
    
            "varying vec4 vertColor;",
            "varying vec2 vertTexCoord;",
    
            "void main() {",
                "float minDist = min(min(vertTexCoord.x, vertTexCoord.y), min(1.0 - vertTexCoord.x, 1.0 - vertTexCoord.y));",
                "float edgeIntensity = smoothstep(0.02, 0.04, minDist);",
                "gl_FragColor = mix(vec4(0.0, 1.0, 0.5, 1.0), vertColor, edgeIntensity);",
            "}"
        };
        shader(new PShader(this, vertSource, fragSource));
    }
    
    public void renderLandscape() {
        PVector vecA = new PVector(), vecB = new PVector(), vecC = new PVector(), vecD = new PVector();
        float colA, colB, colC, colD;
        offset = frameCount * 0.003;
        beginShape(QUADS);
        for(int z1 = -5, z2 = -4; z1 < 5; ++z1, ++z2)
            for(int x1 = -5, x2 = -4; x1 < 5; ++x1, ++x2) {
                float y = noise(x1 * 0.2 + offset, z1 * 0.2 + offset);
                colA = 127 - y * 127;
                vecA.set(x1, y * 5, z1).mult(100);
                y = noise(x2 * 0.2 + offset, z1 * 0.2 + offset);
                colB = 127 - y * 127;
                vecB.set(x2, y * 5, z1).mult(100);
                y = noise(x2 * 0.2 + offset, z2 * 0.2 + offset);
                colC = 127 - y * 127;
                vecC.set(x2, y * 5, z2).mult(100);
                y = noise(x1 * 0.2 + offset, z2 * 0.2 + offset);
                colD = 127 - y * 127;
                vecD.set(x1, y * 5, z2).mult(100);
                fill(colA);
                vertex(vecA.x, vecA.y, vecA.z, 0.0, 0.0);
                fill(colB);
                vertex(vecB.x, vecB.y, vecB.z, 1.0, 0.0);
                fill(colC);
                vertex(vecC.x, vecC.y, vecC.z, 1.0, 1.0);
                fill(colD);
                vertex(vecD.x, vecD.y, vecD.z, 0.0, 1.0);
            }
        endShape();
    }
    

    If you are searching for a more sofistcated solution instead, have a look into Solid Wireframe:

    solidWireframe

    PShader myShader;
    float angle = 0.0;
    float offset;
    
    void setup() {
        size(800, 800, P3D);
    
        // Without this hint Processing will premultiply vertex with the modelview matrix
        // before sending it to the vertex shader (for performance reasons). If you notice
        // any slowdowns, remove the hint and uncomment "updateModelviewMatrix", "uniform
        // mat4 model" in the vertex shader and replace other the 2 lines in the shader
        // with their respective comments.
        hint(DISABLE_OPTIMIZED_STROKE);
    
        noStroke();
        initShader();
    }
    
    void draw() {
        background(0);
        camera(sin(angle += 0.0005) * 800, -800, cos(angle) * 800, 0, 500, 0, 0, 1, 0);
        //updateModelviewMatrix();
        renderLandscape();
    }
    
    //void updateModelviewMatrix() {
    //    PMatrix3D modelview = ((PGraphicsOpenGL)g).modelview;
    //    // PShader.set(String, PMatrix3D) doesn't (for whatever reason) convert from
    //    // Processing's to OpenGL's matrix format, so we have to do it ourself. 
    //    PMatrix3D model = new PMatrix3D(modelview.m00, modelview.m10, modelview.m20, modelview.m30,
    //                                    modelview.m01, modelview.m11, modelview.m21, modelview.m31,
    //                                    modelview.m02, modelview.m12, modelview.m22, modelview.m32,
    //                                    modelview.m03, modelview.m13, modelview.m23, modelview.m33);
    //    myShader.set("model", model);
    //}
    
    void initShader() {
        String[] vertSource = {
            "#version 120",
    
            //"uniform mat4 model;",
            "uniform mat4 transform;",
            "uniform vec4 viewport;",
    
            "attribute vec4 vertex;",
            "attribute vec4 color;",
            "attribute vec2 texCoord;",
            "attribute vec4 p1_3d;",
            "attribute vec4 p2_3d;",
    
            "varying vec4 vertColor;",
            "varying vec3 dist;",
    
            "void main() {",
                "vertColor = color;",
                "gl_Position = transform * vertex;",
                "vec2 p0 = gl_Position.xy / gl_Position.w;",
                "vec4 p1_3d_ = transform * p1_3d;", //"vec4 p1_3d_ = transform * (model * p1_3d);",
                "vec2 v1 = viewport.zw * (p1_3d_.xy / p1_3d_.w - p0);",
                "vec4 p2_3d_ = transform * p2_3d;", //"vec4 p2_3d_ = transform * (model * p2_3d);",
                "vec2 v2 = viewport.zw * (p2_3d_.xy / p2_3d_.w - p0);",
                "float h = abs(v1.x * v2.y - v1.y * v2.x) / length(v1 - v2);",
                "if(texCoord.x < 0.1)",
                    "dist = vec3(h, 0, 0);",
                "else if(texCoord.x < 1.1)",
                    "dist = vec3(0, h, 0);",
                "else",
                    "dist = vec3(0, 0, h);",
                "dist *= gl_Position.w;",
            "}"
        };
        String[] fragSource = {
            "#version 120",
    
            "uniform vec4 wireColor;",
    
            "varying vec4 vertColor;",
            "varying vec3 dist;",
    
            "void main(void) {",
                "vec3 dist_vec = dist * gl_FragCoord.w;",
                "float minDist = min(dist_vec.x, min(dist_vec.y, dist_vec.z));",
                "float edgeIntensity = exp2(-0.3 * minDist * minDist);",
                "gl_FragColor = mix(vertColor, wireColor, edgeIntensity);",
            "}"
        };
        myShader = new PShader(this, vertSource, fragSource);
        shader(myShader);
        myShader.set("wireColor", 0.0, 1.0, 0.5, 1.0);
    }
    
    void renderLandscape() {
        PVector vecA = new PVector(), vecB = new PVector(), vecC = new PVector(), vecD = new PVector();
        float colA, colB, colC, colD;
        offset = frameCount * 0.003;
        beginShape(TRIANGLES);
        for(int z1 = -5, z2 = -4; z1 < 5; ++z1, ++z2)
            for(int x1 = -5, x2 = -4; x1 < 5; ++x1, ++x2) {
    
                // Generate the coordinates and colors of the quad edges
                float y = noise(x1 * 0.2 + offset, z1 * 0.2 + offset);
                colA = 127 - y * 127;
                vecA.set(x1, y * 5, z1).mult(100);
                y = noise(x2 * 0.2 + offset, z1 * 0.2 + offset);
                colB = 127 - y * 127;
                vecB.set(x2, y * 5, z1).mult(100);
                y = noise(x2 * 0.2 + offset, z2 * 0.2 + offset);
                colC = 127 - y * 127;
                vecC.set(x2, y * 5, z2).mult(100);
                y = noise(x1 * 0.2 + offset, z2 * 0.2 + offset);
                colD = 127 - y * 127;
                vecD.set(x1, y * 5, z2).mult(100);
                fill(colA);
    
                // Send the vertex data to the shader. Each vertex needs both of the other triangle-vertices to create
                // the wireframe. The U of the UV-coordinates is used as unique index inside the triangle.
                attrib("p1_3d", vecB.x, vecB.y, vecB.z, 1.0);
                attrib("p2_3d", vecC.x, vecC.y, vecC.z, 1.0);
                vertex(vecA.x, vecA.y, vecA.z, 0.0, 0.0);
                fill(colB);
                attrib("p2_3d", vecC.x, vecC.y, vecC.z, 1.0);
                attrib("p1_3d", vecA.x, vecA.y, vecA.z, 1.0);
                vertex(vecB.x, vecB.y, vecB.z, 1.0, 0.0);
                fill(colC);
                attrib("p2_3d", vecA.x, vecA.y, vecA.z, 1.0);
                attrib("p1_3d", vecB.x, vecB.y, vecB.z, 1.0);
                vertex(vecC.x, vecC.y, vecC.z, 2.0, 0.0);
                fill(colD);
                attrib("p1_3d", vecA.x, vecA.y, vecA.z, 1.0);
                attrib("p2_3d", vecC.x, vecC.y, vecC.z, 1.0);
                vertex(vecD.x, vecD.y, vecD.z, 0.0, 0.0);
                fill(colA);
                attrib("p1_3d", vecC.x, vecC.y, vecC.z, 1.0);
                attrib("p2_3d", vecD.x, vecD.y, vecD.z, 1.0);
                vertex(vecA.x, vecA.y, vecA.z, 1.0, 0.0);
                fill(colC);
                attrib("p1_3d", vecD.x, vecD.y, vecD.z, 1.0);
                attrib("p2_3d", vecA.x, vecA.y, vecA.z, 1.0);
                vertex(vecC.x, vecC.y, vecC.z, 2.0, 0.0);
    
            }
        endShape();
    }
    

    Btw.: Of course this can be used display quad-wires as well, but that's up to you. ;)

  • edited September 2015

    I get the same error on both of your examples :

    Cannot invoke mult(int) on the primitive type void
    
    vecA.set(x1, y * 5, z1).mult(100);
    

    Any idea ? I'm using Processing 2.2.1

  • That's because 2.2.1's PVector class didn't support method chaining. You'd have to split this into two operations to make it work:

    vecA.set(x1, y * 5, z1);
    vecA.mult(100);
    

    But I don't think there was an attrib() equivalent back in 2.2.1...

  • Great examples! Thank you!

Sign In or Register to comment.