I'm developing a game using Sprite Kit and Liquid Fun (Google's implementation of Box2d with liquids).
After I applied a threshold shader to make the liquid more real, I stumbled in a strange behavior that can be seen on this video: Current Behavior
The way it should behave can be seen on this video: Normal Behavior
I'm applying the shader on a SKEffectNode that contains all the particles (SKSpriteNodes)
That's the code on my threshold shader:
void main()
{
vec4 color = texture2D(u_texture,v_tex_coord);
if(color.w > 0.4) {
color = vec4(18.0/255.0, 122.0/255.0, 232.0/255.0, 1.0);
} else {
color = vec4(0.0, 0.0, 0.0, 0.0);
}
gl_FragColor = color;
}
Does anyone have any clue of what is going on?
Related
Is there a way, possibly a ccBlendFunc, that will allow me to dynamically color sprites without affecting the pure white (255, 255, 255), pure black (0, 0, 0), and purely transparent (alpha=255) pixels?
Using the default blend function and setting sprite.color on a CCSprite will re-color the white pixels to whatever value is ccColor3B value is specified, and that is undesirable for me.
Use a shader. If you are using cocos2d version 2.1, start with ccShader_PositionTextureColor_frag (used by CCSprite to render textures, and other classes), copied here
#ifdef GL_ES
precision lowp float;
#endif
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
uniform sampler2D CC_Texture0;
void main()
{
gl_FragColor = v_fragmentColor * texture2D(CC_Texture0, v_texCoord);
}
You want to change that line in main() to skip the fragments you want to skip. CCSprite writes the 'sprite.color' property into v_fragmentColor (look at the code , there are 'premultiplied alpha' variants). You want to modify the v_fragmentColor when texture2D(CC_Texture0, v_texCoord).a == 0, and other circumstances.
I would extend CCSprite to use this new shader (ie avoid toying directly with the shaders builtin to cocos2d, have your own trial and error place). Once you have the shader doing what you want, add the logic in your class to place the new shader program in CCShaderCache, and retrieve it from there.
I have a simple GLSL texture renderer:
Vertex shader:
varying vec2 UV;
void main() {
UV = gl_MultiTexCoord0.xy;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
Fragment shader:
varying vec2 UV;
uniform sampler2D diffuseMap;
void main(void) {
gl_FragColor = texture2D(diffuseMap, UV);
}
And I have texture made of solid colors. I need to render this texture without any interpolation or antialiasing (which seems to happen at the edges of the solid colors). For me it would be better to just take the nearest pixel, rather than try to interpolate.
I'm not sure I was clear. Imagine it like this: I want to texture a ball with a chess pattern, and I want the result to be pure black and white. But the rendered creates a little bit of gray where black and white meet.
Set GL_TEXTURE_MIN_FILTER and GL_TEXTURE_MAX_FILTER to GL_NEAREST and make sure that you do not have mipmaps.
I have some functions that use opengl to draw lines on the screen (health bars) and I recently moved from opengl es 1.1 to opengl es 2.0. I was using glColor4ub(50,160,50,255); to change the line color to green before rendering it on the screen, but that function appears to not exist in version 2.0. It says it is not valid and just renders all lines white.
Is there a different way I should be changing line colors? I've tried looking it up, but there doesn't seem to be anyone with the same question. It may be something simple I'm not seeing.
My game uses cocos2d 2.0 and the health bars are pretty much the only thing that I directly use opengl to render. Everything else is using sprite sheets and such. Thanks for any help.
As with everything else in OpenGL ES 2.0, you need to send the line colour to your fragment shader (via the vertex shader as a vertex attribute, or directly as uniform) and output the colour in your fragment shader.
In the simplest case, you could use a static value in the fragment shader:
void main(void) {
gl_FragColor = vec4(50.0/255.0, 160.0/255.0, 50.0/255.0, 255.0/255.0);
}
If you want to vary the colour at run time, send a value to the fragment shader as a uniform:
GLfloat color[4];
color[0] = 50.0/255.0;
color[1] = 160.0/255.0;
color[2] = 50.0/255.0;
color[3] = 255.0/255.0;
GLint lineColorSlot = glGetUniformLocation(shaderProgram, "LineColor");
glUniformMatrix4fv(lineColorSlot, 1, 0, color);
The fragment shader:
uniform lowp vec4 LineColor;
void main(void) {
gl_FragColor = LineColor;
}
To my surprise, the Image component has no radius property. I tried emulating the rounded corners by putting the image in a rounded Rectangle, but it does not clip the corners.
Rectangle {
anchors.right: rectContentBg.left
anchors.top: rectContentBg.top
anchors.margins: 8
radius: 8
width: 64
height: 64
Image {
id: imgAuthor
opacity: 1
smooth: false
anchors.fill: parent
source: "qrc:/res/sample_avatar.jpg"
}
}
How can I create an image with rounded corners properly?
A built-in official solution exists as of Qt 5 thanks to the QtGraphicalEffects module and I'm quite surprised to find out that no one provided such a simple solution. If you are targeting Qt 6.x QtGraphicalEffects is unfortunately deprecated so jump to the second part of the answer which proposes a solution independent of QtGraphicalEffects.
QtGraphicalEffects solution
Among the other effects OpacityMask is the type to be exploited for this purpose. The idea is to mask the source Image with a Rectangle that has a correctly set radius. Here goes the simplest example using layering:
Image {
id: img
property bool rounded: true
property bool adapt: true
layer.enabled: rounded
layer.effect: OpacityMask {
maskSource: Item {
width: img.width
height: img.height
Rectangle {
anchors.centerIn: parent
width: img.adapt ? img.width : Math.min(img.width, img.height)
height: img.adapt ? img.height : width
radius: Math.min(width, height)
}
}
}
}
This minimum code produces a nice result for square images but
it also takes in account non-square images via the adapt variable. By setting the flag to false the produced mask will always be a circle, regardless of the image size. That is possible due to the usage of an external Item which fills the source and allows the real mask (the inner Rectangle) to be sized at please. You can obviously get rid of the external Item, if you simply aim to a mask that fills the source, regardless of the its aspect ratio.
Here is a cute cat image with a square format (left), a non-square format with adapt: true (center) and finally a non-square format and adapt: false (right):
The implementation details of this solution are very similar to those of the shader-based answer in the other nice answer (cfr. the QML source code for OpacityMask that can be found here - SourceProxy simply returns a well-formed ShaderEffectSource to feed the effect).
No-dep solution
If you don't want to - or can't - depend on the QtGraphicalEffects module (well, on the presence of OpacityMask.qml actually), you can reimplement the effect with shaders. Apart from the already provided solution another approach is to use step, smoothstep and fwidth functions. Here is the code:
import QtQuick 2.5
Image {
id: image
property bool rounded: true
property bool adapt: true
layer.enabled: rounded
layer.effect: ShaderEffect {
property real adjustX: image.adapt ? Math.max(width / height, 1) : 1
property real adjustY: image.adapt ? Math.max(1 / (width / height), 1) : 1
fragmentShader: "
#ifdef GL_ES
precision lowp float;
#endif // GL_ES
varying highp vec2 qt_TexCoord0;
uniform highp float qt_Opacity;
uniform lowp sampler2D source;
uniform lowp float adjustX;
uniform lowp float adjustY;
void main(void) {
lowp float x, y;
x = (qt_TexCoord0.x - 0.5) * adjustX;
y = (qt_TexCoord0.y - 0.5) * adjustY;
float delta = adjustX != 1.0 ? fwidth(y) / 2.0 : fwidth(x) / 2.0;
gl_FragColor = texture2D(source, qt_TexCoord0).rgba
* step(x * x + y * y, 0.25)
* smoothstep((x * x + y * y) , 0.25 + delta, 0.25)
* qt_Opacity;
}"
}
}
Similarly to the first approach, rounded and adapt properties are added to control the visual appearance of the effect as discussed above.
This code would help you
Rectangle {
width: 200
height: 200
color: "transparent"
//this Rectangle is needed to keep the source image's fillMode
Rectangle {
id: imageSource
anchors.fill: parent
Image {
anchors.fill: parent
source: "your_image_file_path"
fillMode: Image.PreserveAspectCrop
}
visible: false
layer.enabled: true
}
Rectangle {
id: maskLayer
anchors.fill: parent
radius: parent.width / 2
color: "red"
border.color: "black"
layer.enabled: true
layer.samplerName: "maskSource"
layer.effect: ShaderEffect {
property var colorSource: imageSource
fragmentShader: "
uniform lowp sampler2D colorSource;
uniform lowp sampler2D maskSource;
uniform lowp float qt_Opacity;
varying highp vec2 qt_TexCoord0;
void main() {
gl_FragColor =
texture2D(colorSource, qt_TexCoord0)
* texture2D(maskSource, qt_TexCoord0).a
* qt_Opacity;
}
"
}
}
// only draw border line
Rectangle {
anchors.fill: parent
radius: parent.width / 2
border.color: "black"
border.width: 2
color: "transparent"
}
}
When your background is a solid color or when you're never moving the image, a fast way to make rounded corners is to overlap your Image with another one (or with a BorderImage) that only draws the corners.
When this is not an option, but you are using OpenGL, then another way is to apply a mask to the image through a pixel shader. See http://blog.qt.digia.com/blog/2011/05/03/qml-shadereffectitem-on-qgraphicsview/ for a plugin that works on top of Qt 4.
Finally, it's also possible to write a QDeclarativeImageProvider that preprocesses your image to make the corners rounded.
If you have a unicolor background, you can draw with the border of a rounded rectangle on top.
Image{
id:img
}
Rectangle { // rounded corners for img
anchors.fill: img
color: "transparent"
border.color: "blue" // color of background
border.width: 4
radius: 4
}
While both the accepted answer and the one from #fury worked equally well for me (Qt 5.9.3), they both left some aberrations in the corners when applied to raster images (didn't have those with SVG). What worked best for me in all cases was to apply the OpacityMask to a surrounding item, e.g. like the rectangle in the original post.
Rectangle {
id: root;
anchors.right: rectContentBg.left
anchors.top: rectContentBg.top
anchors.margins: 8
radius: 8
width: 64
height: 64
// apply rounded corners mask
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Rectangle {
x: root.x; y: root.y
width: root.width
height: root.height
radius: root.radius
}
}
Image {
id: imgAuthor
opacity: 1
smooth: false
anchors.fill: parent
source: "qrc:/res/sample_avatar.jpg"
}
}
QML currently supports only rectangular clipping, but you might want to take a look at DeclarativeMaskedImage in qt-components project:
http://qt.gitorious.org/qt-components/qt-components/blobs/master/src/symbian/sdeclarativemaskedimage.h
I know I'm a little late to the party, but I got here by googling, so thought I'd help future generations :) QtGraphicalEffects OpacityMask should do this a bit more simply (I had issues with the layer effect approach)
Image {
id: imgAuthor
width: 64
height: 64
source: "qrc:/res/sample_avatar.jpg"
visible: false // this is needed or the corners of the image will be visible underneath the opacity mask
}
OpacityMask {
anchors.fill: imgAuthor
source: imgAuthor
maskSource: Rectangle {
width: imgAuthor.width
height: imgAuthor.height
radius: 8
visible: false // this also needs to be invisible or it will cover up the image
}
}
If you have a unicolor background, you can draw with the border of a rounded rectangle on top.
Item {
property int radius: 0
property color bgColor: "#000000"
property int drawRadius: radius > 0 ? radius : width/2
Image {
anchors.fill: parent
sourceSize: Qt.size(width, height)
asynchronous: true
}
Canvas {
anchors.fill: parent
antialiasing: true
onPaint: {
var ctx = getContext("2d")
ctx.fillStyle = bgColor
ctx.beginPath()
ctx.rect(0, 0, width, height)
ctx.fill()
ctx.beginPath()
ctx.globalCompositeOperation = 'source-out'
ctx.roundedRect(0, 0, width, height, drawRadius, drawRadius)
ctx.fill()
}
}
}
I have a CGPath in some coordinate system that I'd like to draw. Doing so involves scaling the old coordinate system onto the Context's one. For that purpose, I use CGContextConcatCTM() which does transform all the points as it should. But, as it is a scaling operation, the horizontal/vertical line widths get changed to. E.g. a scale of 10 in x-direction, but of 1 in y-direction would lead to vertical lines being 10 times as thick as horizontal ones. Is there a way to keep the ease of use of translation matrices (e.g. CGAffineTransform) but not scaling line widths at the same time, e.g. a function like CGPathApplyAffineTransformToPoints?
Cheers
MrMage
Do the transform when you add the path, but then remove the transform before you stroke the path. Instead of this:
CGContextSaveGState(ctx);
CGContextScaleCTM(ctx, 10, 10); // scale path 10x
CGContextAddPath(ctx, somePath);
CGContextSetStrokeColorWithColor(ctx, someColor);
CGContextSetLineWidth(ctx, someWidth); // uh-oh, line width is 10x, too
CGContextStrokePath(ctx);
CGContextRestoreGState(ctx); // back to normal
Do this:
CGContextSaveGState(ctx);
CGContextScaleCTM(ctx, 10, 10); // scale path 10x
CGContextAddPath(ctx, somePath);
CGContextRestoreGState(ctx); // back to normal
CGContextSetStrokeColorWithColor(ctx, someColor);
CGContextSetLineWidth(ctx, someWidth);
CGContextStrokePath(ctx);
You can use CGPathApply to iterate through the elements in a path. It's a little bit more complex than just a one-liner but if you package it up in a simple helper function, it might be useful for you. Here is one version that creates a new path and transforms it:
typedef struct {
CGMutablePathRef path;
CGAffineTransform transform;
} PathTransformInfo;
static void
PathTransformer(void *info, const CGPathElement *element)
{
PathTransformInfo *transformerInfo = info;
switch (element->type) {
case kCGPathElementMoveToPoint:
CGPathMoveToPoint(transformerInfo->path, &transformerInfo->transform,
element->points[0].x, element->points[0].y);
break;
case kCGPathElementAddLineToPoint:
CGPathAddLineToPoint(transformerInfo->path, &transformerInfo->transform,
element->points[0].x, element->points[0].y);
break;
case kCGPathElementAddQuadCurveToPoint:
CGPathAddQuadCurveToPoint(transformerInfo->path, &transformerInfo->transform,
element->points[0].x, element->points[0].y,
element->points[1].x, element->points[1].y);
break;
case kCGPathElementAddCurveToPoint:
CGPathAddCurveToPoint(transformerInfo->path, &transformerInfo->transform,
element->points[0].x, element->points[0].y,
element->points[1].x, element->points[1].y,
element->points[2].x, element->points[2].y);
break;
case kCGPathElementCloseSubpath:
CGPathCloseSubpath(transformerInfo->path);
break;
}
}
To use it you would do (this is the part I would put inside a helper function):
PathTransformInfo info;
info.path = CGPathCreateMutable();
info.transform = CGAffineTransformMakeScale(2, 1);
CGPathApply(originalPath, &info, PathTransformer);
The transformed path is in info.path at this point.