OpenGL blend function that multiplies by srcAlpha AND dstAlpha (and its inverse) - opengl

Basically I want the following blend function:
newFragColor = SrcColor * SrcAlpha * DstAlpha + DstColor * (1-(SrcAlpha * DstAlpha))
Can I achieve that somehow using glBlendFunc and glBlendEquation?
The left side of the addition is easy - for that I just need to premultiply the fragment color with the fragment alpha and then use GL_DST_ALPHA as the first parameter to glBlendFunc. But how can I achieve the right side of the addition?

Related

Packing and unpacking a uint into float4 in DirectX

I have a texture atlas that I'm generating from an array of uints. Sampling from it in my pixel shader, colors are coming out correctly. Here's the relevant HLSL:
Texture2D textureAtlas : register(t8);
SamplerState smoothSampler : register(s9)
{
Filter = MIN_MAG_MIP_LINEAR;
AddressU = Clamp;
AddressV = Clamp;
}
struct PS_OUTPUT
{
float4 Color : SV_TARGET0;
float Depth : SV_DEPTH0;
}
PS_OUTPUT PixelShader
{
// among other things, u and v are calculated here
output.Color = textureAtlas.Sample(smoothSampler, float2(u,v));
}
This works great. With color working, I've extended the texture atlas to include depth information as well. There are only a few thousand depth values that I want, well under 24 bits worth (my depth buffer is 24 bits wide + an 8 bit stencil). The input depth values are uints, just like the colors, though of course in the depth case the values are going to be spread over four color channels and in the shader I want a single float between 0 and 1, so that will need to be computed from the sample. Here's the additional pixel shader code:
// u and v are recalculated for the depth portion of the texture atlas
float4 depthSample = textureAtlas.Sample(smoothSampler, float2(u,v));
float depthValue =
(depthSample.b * 65536.0 +
depthSample.g * 256.0 +
depthSample.r)
/ 65793.003921568627450980392156863;
output.Depth = depthValue;
The long constant here is 16777216/255, which should map the full uint range down to a unorm.
Now, when I'm generating the texture, if I constrain the depth values to the range of 0..2048, the output depth is correct. However, if I allow the upper limit of the range to increase (even if it's simply by taking the input values and performing a left shift by 16), then the output depths will be slightly off. Not by much, just +/- 0.002, but it's enough to make the output look terrible.
Can anybody spot my bug here? Or, more generally, is there a better way of packing and unpacking uints into textures?
I'm working in shader model 4 level 9_3 and C++ 11.
Your code is prone to precision loss: you're adding a relatively large number up to (65536+256) and a small number depthSample.r < 1.
Also, make sure your (u,v) are in the center of the texel to avoid filtering or replace Sample with Load.
Since you're using SM4 you can use the functions asuint and asfloat to reinterpret cast.
You can also use float format textures instead of R8G8B8A8.

Can someone please explain this Fragment Shader? It is a Chroma Key Filter (Green screen effect)

I'm trying to understand how this chroma key filter works. Chroma Key, if you don't know, is a green screen effect. Would someone be able to explain how some of these functions work and what they are doing exactly?
float maskY = 0.2989 * colorToReplace.r + 0.5866 * colorToReplace.g + 0.1145 * colorToReplace.b;
float maskCr = 0.7132 * (colorToReplace.r - maskY);
float maskCb = 0.5647 * (colorToReplace.b - maskY);
float Y = 0.2989 * textureColor.r + 0.5866 * textureColor.g + 0.1145 * textureColor.b;
float Cr = 0.7132 * (textureColor.r - Y);
float Cb = 0.5647 * (textureColor.b - Y);
float blendValue = smoothstep(thresholdSensitivity, thresholdSensitivity + smoothing, distance(vec2(Cr, Cb), vec2(maskCr, maskCb)));
gl_FragColor = vec4(textureColor.rgb * blendValue, 1.0 * blendValue);
I understand the first 6 lines (converting the color to replace, which is probably green, and the texture color to the YCrCb color system).
This fragment shader has two input float values: thresholdSensitivity and Smoothing.
Threshold Sensitivity controls how similar pixels need to be colored to be replaced.
Smoothing controls how gradually similar colors are replaced in the image.
I don't understand how those values are used in the blendValue line. What does blendValue compute? How does the blendValue line and the gl_FragColor line actually create the green screen effect?
The smoothstep function in GLSL evaluates a smooth cubic curve over an interval (specified by the first two parameters). As compared to GLSL's mix function, which linearly blends its parameters as:
smoothstep uses a Hermite cubic polynomial to determine the value
In your shader, blendValue is a smooth interpolation of your smoothing value based on the distance between the red and blue chrominance values.
Finally, gl_FragColor specifies the final fragment color (before blending, which occurs after completion of the fragment shader). In your case, it's the modulated value read from the input image, and a modulated alpha value for translucency.

Drawing anti-aliased lines without color change due to background?

All the anti-aliased line drawing algorithms I've come across simply say that the "intensity" of the pixels needs to be a function of how much of the line passes through it. This works fine on constant backgrounds (ie white), but I want to be able to draw on a background of arbitrary complexity, which means replacing intensity with transparency and alpha blending the line with the background.
Doing this necessarily changes the color of the line depending on what the background is, since for a 1px line it rarely passes exactly through a single pixel, giving it full opacity. I'm curious if there's a technique for drawing these blended lines while maintaining the appearance of the original color.
Here's an example of my rendering attempt on a colorful background. You'll note the vertical/horizontal lines are drawn as a special case with the real color, and the anti-aliased diagonal lines have a blue tint to them.
Is there a proper way to blend anti-aliased lines into the background while maintaining the appearance of the proper line color?
Edit: and code for actually plotting points:
// Plot pixel at (x,y) with color at transparency alpha [0,1]
static inline void plot(pixel_t *pixels, uint16_t stride, const pixel_t &color, uint16_t x, uint16_t y, uint8_t alpha) {
pixel_t pix = pixels[y*stride+x];
pixels[y*stride+x].r = (uint16_t)color.r * alpha/255 + pix.r * (255 - alpha) / 255;
pixels[y*stride+x].g = (uint16_t)color.g * alpha/255 + pix.g * (255 - alpha) / 255;
pixels[y*stride+x].b = (uint16_t)color.b * alpha/255 + pix.g * (255 - alpha) / 255;
}
Edit: For future generations, blending green and blue can give your lines a blue-ish tint.
I'm glad you spotted the bug in your code.
Another problem to watch out for is gamma correction. Anti-aliasing must be applied in a linear color space to look correct, but most of the time to save some processing steps it is applied in a gamma-corrected color space instead. The effects are much more subtle than your example.

Direct3D rendering 2D images with "multiply" blending mode and alpha

I'm trying to replicate the Photoshop filter multiply with Direct3D. I've been reading and googling about the different render states and I've got the effect almost working. The problem is that it's ignoring the alpha value of the textures.
Here's an image that explains the sitution:
http://www.kloonigames.com/petri/stackoverflow_doesnt_allow_.jpg
I found one solution to this, which was to save the images with no transparency and white background. But I'm not satisfied with this solution. The problem is that I really need to use the alpha value. I want to fade out the images gradually. And I cannot do this if the blending mode is ignoring the alpha value.
So the question is how to render the images with alpha?
Here's the blending mode code:
dev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
dev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_SRCCOLOR);
Edit added the SetTextureStageState
dev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
dev->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
dev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
dev->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
You can achieve this effect in one step by premultipling alpha in your pixel shader, or by using textures with pre-multiplied alpha.
For example if you have 3 possible blend operations for a shader, and you want each one to take alpha into account.
Blend = ( src.rgb * src.a ) + ( dest.rgb * (1-src.a) )
Add = ( src.rgb * src.a ) + ( dest.rgb )
Multiply = (src.rgb * dest.rgb * src.a) + (dest.rgb * (1-src.a) )
You'll notice that Multiply is impossible with a single pass because there are two operations on the source color. But if you premultiply alpha in your shader you can extract the alpha component from the blending operation and it becomes possible to blend all three operations in the same shader.
In your pixel shader you can pre-multiply alpha manually. Or use a tool like DirectXTex texconv to modify your textures.
return float4(color.rgb*color.a, color.a);
The operations become:
Blend = ( src.rgb ) + ( dest.rgb * (1-src.a) )
Add = ( src.rgb ) + ( dest.rgb )
Multiply = ( src.rgb * dest.rgb ) + (dest.rgb * (1-src.a) )
It sounds like you want:
dst.rgb = (src.a * src.rgb) * ((1 - src.a) * dst.rgb)
You would use D3DRS_BLENDOP to do that, but unfortunately there isn't a D3DBLENDOP_MULTIPLY. I don't think this operation is possible without a fragment shader.
OK this is not as simple as you would think. I would use an Effect & two renderTargets for this...
I'm amusing your using one render pass to try to do this, which will not work.
Photoshop has layers & each layers have an alpha channel. BTW it would be nice to know what kind of app your making.
So first in D3D I would create 2 RGBA_32bit renderTargets of the same size as your window & clear them to color white. Make it an array like so (new RenderTarget[2];) for swapping.
Now set the blending state to (AlphaFunc=Add, Src=SrcAlpha, Dst=InvSrcAlpha). For the first circle you draw it into renderTarget[0] using renderTarget[1] as a texture/sampler input source. You will render the circle with an Effect that will take the circles color & multiply it with renderTarget[1]'s sampler color. After you draw circle one you swap the renderTarget[0] with renderTarget[1] by simple indexing, so now renderTarget[1] is the one you draw to & renderTarget[0] is the one you sample from. Then you repeat the drawing process for circle 2 & so on.
After you draw ever circle you copy the last drawn renderTarget to the backBuffer & present the scene.
Here is an example of logically how you would do it. If you need reference for coding http://www.codesampler.com/ is a good place.
void TestLayering()
{
bool rtIndex = false;
RenderTarget* renderTarget = new RenderTarget[2];
Effect effect = new Effect("multiplyEffect.fx");
effect.Enable();
BlendingFunc = Add;
BlendingSource = SrcAlpha;
BlendingDest = InvSrcAlpha;
for(int i = 0; i != circleCount; ++i)
{
renderTarget[rtIndex].EnableAsRenderTarget();
renderTarget[!rtIndex].EnableAsSampler();
circle[i].Draw();
rtIndex = !rtIndex;
}
//Use D3D9's StretchRect for this...
backBuffer.CopyFromSurface(renderTarget[rtIndex]);
}
//Here is the effects pixel shader
float4 PS_Main(InStruct In) : COLOR
{
float4 backGround = tex2D(someSampler, In.UV);
return circleColor * backGround;
}
dev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_DESTCOLOR);
dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
Will do the trick. You cannot use the 'alpha' from the diffuse vertex color anymore though. Setting a low alpha on the vertex colors will actually brighten your overlaying pixels.

How to rotate object around local axis in OpenGL?

I am working on an ongoing project where I want to align the links of a chain so that it follows the contours of a Bezier curve. I am currently following the steps below.
Drawing the curve.
Use a display list to create one link of the chain.
Use a FOR loop to repeatedly call a function that calculates the angle between two points on the curve, returns the angle and the axis around which the link should be rotated.
Rotate by the angle "a" and translate to new position, place the link at the new position.
Edit: I should also say that the centres of the two half torus must lie on the Bezier curve.
Also I am aware that the method I use to draw the torus I tedious, I will use TRIANGLE_FAN or QUAD_STRIP later on to draw the torus in a more efficient way.
While at first glance this logic looks like it would render the chain properly, the end result is not what I had imagined it to be. Here is a picture of what the chain looks like.
I read that you have to translate the object to the origin before rotation? Would I just call glTranslate(0,0,0) and then follow step 4 from above?
I have included the relevant code from what I have done so far, I would appreciate any suggestions to get me code work properly.
/* this function calculates the angle between two vectors oldPoint and new point contain the x,y,z coordinates of the two points,axisOfRot is used to return the x,y,z coordinates of the rotation axis*/
double getAxisAngle(pointType oldPoint[],
pointType newPoint[],pointType axisOfRot[]){
float tmpPoint[3];
float normA = 0.0,normB = 0.0,AB = 0.0,angle=0.0;
int i;
axisOfRot->x= oldPoint->y * newPoint->z - oldPoint->z * newPoint->y;
axisOfRot->y= oldPoint->z * newPoint->x - oldPoint->x * newPoint->z;
axisOfRot->z= oldPoint->x * newPoint->y - oldPoint->y * newPoint->x;
normA=sqrt(oldPoint->x * oldPoint->x + oldPoint->y * oldPoint->y + oldPoint->z *
oldPoint->z);
normB=sqrt(newPoint->x * newPoint->x + newPoint->y * newPoint->y + newPoint->z *
newPoint->z);
tmpPoint[0] = oldPoint->x * newPoint->x;
tmpPoint[1] = oldPoint->y * newPoint->y;
tmpPoint[2] = oldPoint->z * newPoint->z;
for(i=0;i<=2;i++)
AB+=tmpPoint[i];
AB /= (normA * normB);
return angle = (180/PI)*acos(AB);
}
/* this function calculates and returns the next point on the curve give the 4 initial points for the curve, t is the tension of the curve */
void bezierInterpolation(float t,pointType cPoints[],
pointType newPoint[]){
newPoint->x = pow(1 - t, 3) * cPoints[0].x +3 * pow(1 - t , 2) * t * cPoints[1].x + 3
* pow(1 - t, 1) * pow(t, 2) * cPoints[2].x + pow(t, 3) * cPoints[3].x;
newPoint->y = pow(1 - t, 3) * cPoints[0].y +3 * pow(1 - t , 2) * t * cPoints[1].y + 3
* pow(1 - t, 1) * pow(t, 2) * cPoints[2].y + pow(t, 3) * cPoints[3].y;
newPoint->z = pow(1 - t, 3) * cPoints[0].z +3 * pow(1 - t , 2) * t * cPoints[1].z + 3
* pow(1 - t, 1) * pow(t, 2) * cPoints[2].z + pow(t, 3) * cPoints[3].z;
}
/* the two lists below are used to create a single link in a chain, I realize that creating a half torus using cylinders is a bad idea, I will use GL_STRIP or TRIANGLE_FAN once I get the alignment right
*/
torusList=glGenLists(1);
glNewList(torusList,GL_COMPILE);
for (i=0; i<=180; i++)
{
degInRad = i*DEG2RAD;
glPushMatrix();
glTranslatef(cos(degInRad)*radius,sin(degInRad)*radius,0);
glRotated(90,1,0,0);
gluCylinder(quadric,Diameter/2,Diameter/2,Height/5,10,10);
glPopMatrix();
}
glEndList();
/*! create a list for the link , 2 half torus and 2 columns */
linkList = glGenLists(1);
glNewList(linkList, GL_COMPILE);
glPushMatrix();
glCallList(torusList);
glRotatef(90,1,0,0);
glTranslatef(radius,0,0);
gluCylinder(quadric, Diameter/2, Diameter/2, Height,10,10);
glTranslatef(-(radius*2),0,0);
gluCylinder(quadric, Diameter/2, Diameter/2, Height,10,10);
glTranslatef(radius,0, Height);
glRotatef(90,1,0,0);
glCallList(torusList);
glPopMatrix();
glEndList();
Finally here is the code for creating the three links in the chain
t=0.031;
bezierInterpolation(t,cPoints,newPoint);
a=getAxisAngle(oldPoint,newPoint,axisOfRot);
glTranslatef(newPoint->x,newPoint->y,newPoint->z);
glRotatef(a,axisOfRot->x,axisOfRot->y,axisOfRot->z);
glCallList(DLid);
glRotatef(-a,axisOfRot->x,axisOfRot->y,axisOfRot->z);
glTranslatef(-newPoint->x,-newPoint->y,-newPoint->z);
oldPoint[0]=newPoint[0];
bezierInterpolation(t+=GAP,cPoints,newPoint);
a=getAxisAngle(oldPoint,newPoint,axisOfRot);
glTranslatef(newPoint->x,newPoint->y,newPoint->z);
glRotatef(90,0,1,0);
glRotatef(a,axisOfRot->x,axisOfRot->y,axisOfRot->z);
glCallList(DLid);
glRotatef(-a,axisOfRot->x,axisOfRot->y,axisOfRot->z);
glRotatef(90,0,1,0);
glTranslatef(-newPoint->x,-newPoint->y,-newPoint->z);
oldPoint[0]=newPoint[0];
bezierInterpolation(t+=GAP,cPoints,newPoint);
a=getAxisAngle(oldPoint,newPoint,axisOfRot);
glTranslatef(newPoint->x,newPoint->y,newPoint->z);
glRotatef(-a,axisOfRot->x,axisOfRot->y,axisOfRot->z);
glCallList(DLid);
glRotatef(a,axisOfRot->x,axisOfRot->y,axisOfRot->z);
glTranslatef(-newPoint->x,-newPoint->y,newPoint->z);
One thing to note is that glTranslate function builds on previous translations. I.E. a glTranslatef(0.0,0.0,0.0); won't go to the origin, it will just move the "pen" nowhere. Luckily, the "pen" starts at the origin. if you translate out to 1.0,1.0,1.0 then try a glTranslatef(0.0,0.0,0.0); you will still be drawing at 1.0,1.0,1.0;
Also, you seem to grasp the fact that openGL post-multiplies matricies. To that end, you are correctly "undoing" your matrix operations after a draw. I only see one spot where you could potentially be off here and that is in this statement:
glRotatef(90,0,1,0);
glRotatef(a,axisOfRot->x,axisOfRot->y,axisOfRot->z);
glCallList(DLid);
glRotatef(-a,axisOfRot->x,axisOfRot->y,axisOfRot->z);
glRotatef(90,0,1,0);
Here you correctly undo the second rotation, but the first one you seem to rotate even more around the y axis. the very last glRotatef needs to read glRotatef(-90,0,1,0); if you want to be undoing that rotation.
I looked at your code and assuming that code performing bezierInterp and axis angle is correct. Based on code, I have following suggestions:
The way you are creating a single link looks very costly. As you are using gluCylinder for 180 times. This will generate a lot of vertices for a small link. You can create a single torus and apply scale such that it appears like a link!
Whenever you do any matrix operation, it is good idea to set the mode before. This is important before doing push and pop. In you display list you have push and pop without setting any mode and neither it is set in caller. This is not good practice and will result in lot of bugs/issues. You can remove push and pop from call list and keep only geometry in it.
You have heard advice suggesting to do translation to origin before rotation as translation * rotation! = rotation * translation. So the way you would write your render loop is:
// Set matrix mode
glMatrixMode(GL_MODELVIEW);
for(number of links) {
glLoadIdentity(); // makes model view matrix identity - default location`
glTranslatef(x,y,z); // Translate to a point on beizer curve
glRotatef(..); // Rotate link
glCallList(link); // can be simple torus, only geometry centered at origin
}
Above code renders a link repeated at specified location. Read OpenGL Red book's chapter 3 - Example 3.6 (planetary system) example to understand how you can place each link at different location correctly.