WebGL : How do make part of an object transparent? - html

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).
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.

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 :)

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

try add these lines
glEnable (GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
It worked for me with OpenGL ES.

Or remove some triangles from the ball mesh, or apply texture to it that has transparent parts, turn on blending and turn off any type of culling, so you don't loose back-oriented triangles.
EDIT:
Here's a tutorial on blending in webGL: http://learningwebgl.com/blog/?p=859
You have multiple modes to choose from how two pixels will me blended, and by adjusting alpha values you could have anything you want.
Eventually, you apply texture that has alpha set to 1.0 everywhere, except those parts where it should be "hollow" and then enable blending. I hope I made myself clear.
TIP: Instead of reading documentation (which sometimes can be more confusing than helping), try this http://www.nihilogic.dk/labs/webgl_cheat_sheet/WebGL_Cheat_Sheet.htm,. It lists all functions in a very nice manner.

Related

html canvas transform ? is it only: first do the transform, and then you draw into the canvas? Not, stick an image or etc on canvas, and transform it?

My goal is to bring in an image on to part of the canvas, then scale it, move/translate it, and optionally skew it, also rotate and make alpha changes, kind of the primary "2d image manipulations", in an animated form, which is: do little changes over time from the starting state to the target end state.
Well, I figured to be efficient, I should use the canvas/2d context transform, https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/transform -- as it does the first 3: scale, move/translate, and skew, "all in one." I did half that code, and now I'm looking at examples and seeking to debug it. All the examples I see, are do 1) some transform, away from the "unity transform":
{ a:1, b:0, c: 0, d:1, e:0, f:0 }; // this basic transform does nothing
and then 2) draw into that. But that's the opposite order from what I want: which is draw on the canvas (the image), and then do an animation over time using the above primary changes (scale, translate, skew, rotate, and alpha). My question is: does it only "work this way", meaning I must setup the (single) transformation on the page first, and then "draw into that?"
I hope not ... that won't give me what I want, and I have to "ditch it", and go to 5 individual "transformations." Comments?
Yes that only works this way, canvas transforms and compositing mode and filters and lineWidth and fillStyle etc. properties are only applied to the next drawing operations.
The canvas itself only holds pixels information, it has no concept of drawn object. Your js code has to do this part.
So for what you wish, you can simply redraw everything every time:
reset the transform so we can clear ctx.setTransform(1,0,0,1,0,0);
Clear the canvas ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height)
Set the transform to your new matrix ctx.translate(x,y); ctx.scale(s)...
Draw your transformed graphics ctx.fill(); ctx.drawImage(...
Wait next frame to do it again requestAnimationFrame(update)

Line quality is very low with jagged or blurry edges

I'm working on a drawing app and the line quality seems to be very low and jagged compared to other drawing apps.
Or it might be that other apps are doing something different than I'm doing.
What I have done so far is use the graphics property to draw the lines. I also collect the mouse positions on the mouse move events for assigning to a path later on. Summarized it:
MouseDownHandler:
mouseDownPoint.x = event.stageX;
mouseDownPoint.y = event.stageY;
drawCommands.push(GraphicsPathCommand.MOVE_TO);
simplePath = "M " + mouseDownPoint.x + " " + mouseDownPoint.y;
MouseMoveHandler:
line.graphics.lineStyle(lineWeight, lineColor, lineAlpha, pixelHinting);
line.graphics.moveTo(previousPoint.x, previousPoint.y);
scaledPoint = new Point(localPoint.x/scaleX, localPoint.y/scaleY);
line.graphics.lineTo(scaledPoint.x, scaledPoint.y);
previousPoint.x = scaledPoint.x;
previousPoint.y = scaledPoint.y;
simplePath += " L " + scaledPoint.x + " " + scaledPoint.y;
MouseUpHandler:
myPath.data = simplePath;
As I draw I update the line (which is a UIComponent but could just as well be a Shape or Sprite - anything with a graphics property). At the same time I keep track of the mouse locations in the simplePath string.
When the mouse is up I clear the line graphics and show a path graphic element. The Path isn't important to this but I noticed it looks slightly cleaner than the line that had been drawn. That might be because it has pixel hinting (it's not much cleaner). Sometimes there are artifacts. And I'm including it in case I need to use a path for some reason.
Here is the screen shot:
The pixel hinted version looks crisper but it still is far below the quality of the line drawing in other apps and in some cases it makes it look more jagged. Is there something I'm missing?
Note: I included graphics2d and canvas2d because I believe this may not be related to the specific language or platform but might be related to drawing graphics in general.
The green line is produced by Graphics.cubicCurveTo(...) method. Initially you have a list of user-provided points A1,A2,A3...An. In order to use cubic curves you also need to figure 2 control points CFk (forward) and CBk (backward), for each Ak respectively, so you draw that big curve starting from A1 and every curve piece from Ak-1 to Ak will take arguments .cubicCurveTo(CFk-1, CBk, Ak);
For each Ak (except for A1 and An) you can calculate CFk and CBk as following:
(vector)AForward = (vector)(Ak+1 - Ak-1)
(vector)AForward.length = (vector)(Ak+1 - Ak).length / 3
CFk = Ak + (point)AForward
(vector)ABackward = (vector)(Ak-1 - Ak+1)
(vector)ABackward.length = (vector)(Ak-1 - Ak).length / 3
CBk = Ak + (point)ABackward
Then, there are A1 and An that are left out, but I am sure you can figure them on your own.
For vector math you can use ru.delimiter.math.Vector2D class (works with both Cartesian and Polar coordinates) from my small collection of useful things: https://bitbucket.org/thydmitry/ru.delimiter/src/9083fb46ce1c/classes/ru/delimiter/math/
P.S. Maybe you don't need to go that extreme and will be fine with the red line, that is a simple .curveTo(Ak, (Ak + Ak+1)/2);
UPD: a simple algorithm to inscribe a curve into zigzag provided by an array of points.
function middle(A:Point, B:Point):Point
{
return new Point((A.x + B.x) / 2, (A.y + B.y) / 2);
}
function drawTo(target:Point):void
{
graphics.lineTo(target.x, target.y);
}
function bendTo(control:Point, target:Point):void
{
graphics.curveTo(control.x, control.y, target.x, target.y);
}
// This should contain at least 2 points before you start drawing.
var PP:Vector.<Point>;
// Go to the start position.
graphics.lineStyle(0, 0xFF0000);
graphics.moveTo(PP[0].x, PP[0].y);
// Draw a straight line to the center of the first zigzag segment.
drawTo(middle(PP[0], PP[1]));
// For each 3 consequent points A,B and C, connect
// the middle of AB and the middle of BC with a curve.
for (var i:int = 2; i < PP.length; i++)
{
bendTo(PP[i - 1], middle(PP[i - 1], PP[i]));
}
// Connect the center of the last zigzag segment with the end point.
drawTo(PP[PP.length - 1]);
There are multiple reasons:
Stage quality. In Flash Player you can set the stage quality to LOW, MEDIUM, HIGH, BEST, 8x8, 8x8Linear, 16x16 and 16x16Linear. This affects if there is antialiasing applied on lines / paths and how many times it's applied. Increasing the quality helps but in 8x8 and higher quality there are bugs in the Flash Player (font size reduced by 25% on non-embedded fonts, graphics artifacts, gradient fills color count reduced).
Pixel snapping. If you have a 1px line that ends up positioned on a half pixel it is anti aliased over two lines. Normally antialiasing increases quality but in the case of an offset single pixel line it reduces quality. Setting pixel snapping helps.
Using curveTo and cubicCurveTo instead of line points as #Organis suggested. Not sure how to do this yet.
Will try to post images of the difference in each case when I get a chance.

Graphics2D, drawImage() -method, Scala Swing

I am painting on a Panel by overriding the paintComponent -method.
class Canvas extends Panel ...
...
override def paintComponent(g: Graphics2D) {
super.paintComponent(g)
for (cmd <- commands.get) cmd.execute(g, this)
}
Okay. Inside one of the commands is this one.
case class TestCommand() {
def execute(g: java.awt.Graphics2D, c: Canvas) = {
val img = new BufferedImage(c.width, c.height, BufferedImage.TYPE_INT_RGB)
g.drawImage(img, null, 0, 0)
}
What im trying to do there is create a BufferedImage from the Graphics2D -object, so i can read the value of some pixel at location x, y.
However when this command is executed, i notice that the drawImage -method causes my panel to turn completely black, wiping everything i've drawn on it so far. I want the command to simply create a BufferedImage of what i've drawn on the panel, instead of somehow affecting the Graphics2D -object itself. What am i doing wrong here?
A Graphics2D is connected to some canvas or image. You might think of it like a brush or a pen. In this case, the graphics object you have is tied to your panel. Calling drawImage draws the image on to the panel.
If you want to draw the panel on to the image, you need a Graphics for the BufferedImage:
Graphics2D ig = img.createGraphics();
Then you need to paint the panel to the graphics. java.awt.Component provides a number of convenience methods for this type of thing, for example printAll:
c.printAll(ig);
ig.dispose(); // don't forget to dispose Graphics you've created
(Sorry, I don't really know Scala syntax, if it's different.)
Without being too presumptuous, there may be better ways to approach what you're doing. E.g. if you're doing painting to the panel, as mentioned in the comments, it's usually a good idea to do your painting on an image to begin with, then paint the image to the panel. Then you always have the image.

Reverse Clipping in Canvas

I want to clip html5 canvas so that I can achieve drawing result as per following image.
I want to achieve clip path such that all drawing will be performed only in black area.
Method 1
Assuming the white areas are transparent and the black is non-transparent then you can use composite mode:
ctx.globalCompositeOperation = 'source-in';
... draw graphics on top - only solid color will be affected ...
ctx.globalCompositeOperation = 'source-over'; // reset to default mode
A demo fiddle for this here
Method 2
Another simpler approach is to simply fill the canvas with the graphics you need then use clearRect() for those areas you want transparent.
This operation is fairly fast so there shouldn't be any flashing (and in case you can trigger this operation with a single requestAnimationFrame call).
A demo fiddle with clearRect
A demo fiddle with clearRect + requestAnimationFrame
Just note that calling rAF makes the code asynchronous but the purpose of using it is that your draw operations are synchronized within a frame update so flicker will be removed (if you should for some reason get problem with that).
Method 3
Create rectangle regions surrounding the area you want to preserve by a series of call to rect(). The set that as a clipping mask by using clip().
This techniques works best if the non-clipped areas are in a certain order or else you would have to define a lot of regions.
Remember to translate canvas 0.5 pixel first and only use integer values for the rectangles.
Method 4
Parse the pixel buffer manually to fill in pixels in the areas fulfilling the requirements, for example non-transparent pixels only.
Just be aware of that this is probably the slowest approach, it's affected by CORS restrictions (in case you draw an external image onto the canvas first) and it's more tedious if you want to fill in shapes, images, gradients and so forth which in case you would probably prefer an off-screen canvas to copy from.
There are other ways using different composite modes and drawing order to achieve the same result but I leave it with this as it should cover most scenarios.
You can use layering to fill your need:
make a copy of your image with all black made transparent
draw the original image on the canvas
draw your desired shapes
draw the transparent image on top
A Demo: http://jsfiddle.net/m1erickson/dFRUf/
This function creates a temporary canvas with the color-range you specify made transparent:
function makeImageTransparentByColor(image,r1,r2,g1,g2,b1,b2){
// create a temporary canvas and draw the image on the canvas
var bk=document.createElement("canvas");
var bkCtx=bk.getContext("2d");
bk.width=image.width;
bk.height=image.height
bkCtx.drawImage(image,0,0);
// get the pixel array from the canvas
var imgData=bkCtx.getImageData(0,0,bk.width,bk.height);
var data=imgData.data;
// loop through each pixel and make every pixel transparent
// that is between r1-r2, g1-g2 and b1-b2
for(var i=0;i<data.length;i+=4){
var r=data[i];
var g=data[i+1];
var b=data[i+2]
if(r>=r1 && r<=r2 && g>=g1 && g<=g2 && b>=b1 && b<=b2){
data[i]=0;
data[i+1]=0;
data[i+2]=0;
data[i+3]=0;
}
}
// put the modified pixels back on the canvas
bkCtx.putImageData(imgData,0,0);
// return the canvas with transparent image
return(bk);
}

Border around a form with rounded corner in c++ builder XE

I have made a C++ Builder XE form with rounded corner with the help of the following code
BorderStyle = bsNone;
void __fastcall TForm1::FormCreate(TObject *Sender)
{
HRGN frmrgn;
frmrgn = CreateRoundRectRgn (0, 0, ClientWidth, ClientHeight,12,12);
SetWindowRgn(Handle,frmrgn,true);
}
It looks cool but the border is missing, I tried many thing but not get good result
so please help me to draw border of color RGB(96,96,96)
And I want to make whole form dragable.
1. Painting a dark grey border
This one's easy, depending on how complex you want the border to look. If you just want an outline in dark grey, either draw it using a combination of lines and arcs, or use the FrameRgn function to draw an outline around your region with a specific brush. Doing this is the best solution since you already have a region you've used to define the shape of the window.
However, the MSDN documentation for SetWindowRgn says, "After a successful call to SetWindowRgn, the system owns the region specified by the region handle hRgn. The system does not make a copy of the region. Thus, you should not make any further function calls with this region handle." You'll need to create your region again for the paint method.
Some code for your paint method:
HRGN hRegion = ::CreateRoundRectRgn (0, 0, ClientWidth, ClientHeight,12,12);
Canvas->Brush->Style = bsSolid;
Canvas->Brush->Color = RGB(96, 96, 96);
::FrameRgn(Canvas->Handle, hRegion, Canvas->Brush->Handle, 2, 2);
::DeleteObject(hRegion); // Don't leak a GDI object
2. Making the window draggable without its title bar
The short summary is that you need to handle the WM_NCHITTEST message. Windows sends this to see if the mouse is over the title bar ('NC' stands for 'non-client'; it's actually testing to see if it's anywhere in the non-client area, which can be any window border, not just the top one.) You can make your window draggable by saying 'yes, the mouse is in the caption right now' even when it isn't. Some code:
// In the 'protected' section of your form's class declaration
virtual void __fastcall WndProc(Messages::TMessage &Message);
// The implementation of that method:
void __fastcall TForm1::WndProc(Messages::TMessage& Message) {
TForm::WndProc(Message); // inherited implementation
if (Message.Msg == WM_NCHITTEST && Msg.Result == htClient) {
Msg.Result = htCaption;
}
}
You can perform some hit testing of your own to restrict what parts of your window appear to be the title bar, in order to create a title bar of your own.
Example Delphi code.
A good article about using this message, and things to be aware of / traps not to fall into.