Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WebGL : How do make part of an object transparent?

I have a 3D ball in the browser , now I want to dig a hole on it to see its back.

How can I make it possiable ?

For example, I want the white triangle part of the cube could be transparent(I mean we could see the background behind the cube).

enter image description here

I've tried to change the alpha in fragment shader(the area in the code is a square not triangle, doesn't matter):

<script id="shader-fs-alpha" type="x-shader/x-fragment">
    precision mediump float;

    varying vec4 vColor;

    uniform float uAlpha;

    void main(void) {
      if( gl_FragCoord.x < 350.0 && gl_FragCoord.x > 300.0
        && gl_FragCoord.y < 300.0 && gl_FragCoord.y > 230.0
        ) {
        gl_FragColor = vec4(0, 0 ,0, 0.0);
      } else {
        gl_FragColor = vec4(vColor.rgb, 1.0);
      }
    }
</script>

This actually works but the area turns to be white(not transparent), so then I tried to enable blending, but that makes the whole cube transparent.

So now I thought if there's a way to enable bleanding in fragment shader and I could disable it in the else block.

Here's my whole project https://gist.github.com/royguo/5873503:

  • index.html : Shader script here.
  • buffers.js : All obejcts here.
  • shaders.js : Init shaders.
like image 323
WoooHaaaa Avatar asked Jun 22 '13 17:06

WoooHaaaa


3 Answers

You should use stencil buffer. 2 steps: One, draw the triangle on stencil. Two, draw the cube with stencil test.

// you can use this code multiple time without clearing stencil
// just increment mask_id
mask_id = 1;

// allow to draw inside or outside mask
invert_mask = false;

1) Activate stencil buffer. Configure it: no testing and write mask_id value

glEnable(GL_STENCIL_TEST);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glStencilFunc(GL_ALWAYS, mask_id, 0);

2) Remove color & depth writing

glDepthMask(GL_FALSE);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

3) here draw your triangle :)

4) Enabled depth & color writing for the cube

glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthMask(GL_TRUE);

5) Configure stencil for the cube: no writing and test stencil value

glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glStencilFunc(invert_mask ? GL_NOTEQUAL : GL_EQUAL, mask_id, 0xff);

6) here draw your cube :)

7) cleanup: remove stencil test

glDisable(GL_STENCIL_TEST);

Don't forget to request a stencil when you create your opengl context (see WebGLContextAttributes, second parameter of getContext)

This code run well with Opengl ES 2 on Ios platform. It didn't depend of any extension. WebGL use the same API.

The only small defect: no msaa on triangle borders. There is no free lunch :)

like image 65
Vivien Avatar answered Oct 10 '22 16:10

Vivien


You could render part of the scene without the object (or the objects insides) to a texture and then project the texture onto a surface in front of the object. Just make sure that the surface faces the camera directly (is parallel to the x-y-plane in cam-space) and that the texture is rendered with the same center and perspective as the screen.

Use Learning WebGL: Rendering to Textures if you require help doing this. You should also check the light options to make sure the surface itself isn't affected by the lighting that your object gets.

Also: official WebGL reference sheet

like image 29
havarc Avatar answered Oct 10 '22 16:10

havarc


try add these lines

glEnable (GL_BLEND);                                                              
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

It worked for me with OpenGL ES.

like image 42
ene Avatar answered Oct 10 '22 16:10

ene