Non photo-realistic rendering with OpenGL ES

Screenshot_20190725-145012Screenshot_20190725-145000

Inspired by the EDL shading technique and SSAO, I implemented a new shader for OpenGL ES designed to enhance the rendering of point clouds. As the shading function rely only on the projected depth, and because the variable gl_FragDepth is missing in OpenGL ES, a workaround was to implement a custom depth buffer using a frameBuffer object.

In practice, the multi-pass rendering is done in 2 steps:

  1. Create a new frameBuffer and render the scene as usual with this new frameBuffer bound as the active one.
  2. Bind to the default frameBuffer, then draw a quad that spans the entire screen with the new frameBuffer’s color buffer as its texture.

(See https://learnopengl.com/Advanced-OpenGL/Framebuffers for more details)

This shading functions is available as an experimental feature in Lidar VR Viewer.

My final algorithm is inspired by this one

Here are the vertex and fragment shaders:

First pass:

//vertex shader
uniform mat4 MVP;
attribute vec4 a_Position;
attribute vec4 a_Color;
varying vec4 v_Color;

void main()
{
   float f=1000.0;
   float n=0.1;
   gl_Position = MVP * a_Position;
   float depth=((1./gl_Position.z)-(1./n))/((1./f)-(1./n));
   float z = depth * 2.0 - 1.0;
   float linearDepth = (2.0 * n * f) / (f + n - z * (f - n));
   v_Color = vec4(a_Color.xyz, linearDepth/30.);
}

//fragment shader
precision mediump float;
varying vec4 v_Color;

void main() {
vec2 pt = gl_PointCoord - vec2(0.5);
//to draw a circle instead of a square
if(pt.x*pt.x+pt.y*pt.y > 0.25)discard;
gl_FragColor = v_Color;
}

Second pass:

//vertex shader
attribute vec2 a_texCoord0;
attribute vec4 a_vertCoord0;
varying vec2 texCoord;

void main()
{
   gl_Position = vec4(a_vertCoord0.x, a_vertCoord0.y, 0., 1.);
   texCoord = a_texCoord0;
}

//fragment shader
precision highp float;
varying vec2 texCoord;
uniform sampler2D u_color;
const float offset = 1.0 / 300.0;
const float M_PI=3.14159265;

const vec2 agk_resolution=vec2(0.1,0.1);
const float ssaoStrength=1.;
const float ssaoBase=0.2;
const float ssaoArea=0.02;
const float ssaoFalloff=0.000001;
const float ssaoRadius=0.001;
const int samples = 16;

float obscurance(float z, float dist)
{
   return max(0.0, z) / dist;
}

float ztransform(float z_b)
{
   return clamp(1.0 - z_b, 0.0, 1.0);
}

float shade(float r, int div, float dist){
    float h=2038.;
    float w=1080.;
    float s=0.;
    float r2=float(div);
    float ad = 2.*M_PI/float(div);
    for(int i=0;i<div;i++){         float a = float(i)*ad;         vec2 o = vec2(offset*cos(a),  offset*sin(a));         s+=-1.*(texture2D(u_color, texCoord.st + (r/vec2(h,w)*o)).w);     }     float diff = r2*texture2D(u_color, texCoord.st).w +s;     return diff; } vec3 GetNormalFromDepth(float depth, vec2 uv) {     vec2 offset1 = vec2(0.0,1.0/agk_resolution.y);     vec2 offset2 = vec2(1.0/agk_resolution.x,0.0);     float depth1 = texture2D(u_color, uv + offset1).w;     float depth2 = texture2D(u_color, uv + offset2).w;     vec3 p1 = vec3(offset1, depth1 - depth);     vec3 p2 = vec3(offset2, depth2 - depth);     vec3 normal = cross(p1, p2);     normal.z = -normal.z;     return normalize(normal); } void main() {     vec3 color = texture2D(u_color, texCoord).rgb;     float depth = texture2D(u_color, texCoord).w;     if(depth>0.){
        vec3 random = vec3(1.0);

        vec3 position = vec3(texCoord, depth);
        vec3 normal = GetNormalFromDepth(depth, texCoord);

        vec3 sampleSphere[samples];
        sampleSphere[0] = vec3( 0.5381, 0.1856,-0.4319);
        sampleSphere[1] = vec3( 0.1379, 0.2486, 0.4430);
        sampleSphere[2] = vec3( 0.3371, 0.5679,-0.0057);
        sampleSphere[3] = vec3(-0.6999,-0.0451,-0.0019);
        sampleSphere[3] = vec3( 0.0689,-0.1598,-0.8547);
        sampleSphere[5] = vec3( 0.0560, 0.0069,-0.1843);
        sampleSphere[6] = vec3(-0.0146, 0.1402, 0.0762);
        sampleSphere[7] = vec3( 0.0100,-0.1924,-0.0344);
        sampleSphere[8] = vec3(-0.3577,-0.5301,-0.4358);
        sampleSphere[9] = vec3(-0.3169, 0.1063, 0.0158);
        sampleSphere[10] = vec3( 0.0103,-0.5869, 0.0046);
        sampleSphere[11] = vec3(-0.0897,-0.4940, 0.3287);
        sampleSphere[12] = vec3( 0.7119,-0.0154,-0.0918);
        sampleSphere[13] = vec3(-0.0533, 0.0596,-0.5411);
        sampleSphere[14] = vec3( 0.0352,-0.0631, 0.5460);
        sampleSphere[15] = vec3(-0.4776, 0.2847,-0.0271);

        float radiusDepth = ssaoRadius/depth;
        float occlusion = 0.0;
        for(int i=0; i < samples; i++)
        {
            vec3 ray = radiusDepth * reflect(sampleSphere[i], random);
            vec3 hemiRay = position + sign(dot(ray, normal)) * ray;

            float occDepth = texture2D(u_color, clamp(hemiRay.xy, 0.0, 1.0)).w;
            float difference = depth - occDepth;

            occlusion += step(ssaoFalloff, difference) * (1.0 - smoothstep(ssaoFalloff, ssaoArea, difference));

           //float rangeCheck = abs(difference) < radiusDepth ? 1.0 : 0.0;
           //occlusion += (occDepth <= position.z ? 1.0 : 0.0) * rangeCheck;
        }

        float ao = 1.0 - ssaoStrength * occlusion * (1.0/float(samples));

        float ambocc = clamp(ao + ssaoBase, 0.0, 1.0);

        float diff=0.;
        diff=0.5*shade(500.,8,offset)+0.3*shade(800.,8,offset)+0.15*shade(1100.,8,offset)+0.05*shade(1400.,8,offset);
        diff = ztransform(diff);
        diff = pow(diff,4.);

        gl_FragColor = vec4(color*ambocc*diff,1.);
    }else{
        gl_FragColor = vec4(color,1.);
    }
}

 

Comments are closed.

Create a website or blog at WordPress.com

Up ↑

%d bloggers like this: