I'm using the latest cocos2d-x v3.9 (JSBinding).
And here is a code (add a sprite and attach with a simple gray shader):
this.winSize = cc.director.getWinSize();
var sprite = new cc.Sprite(res.png_building_3);
sprite.setPosition(this.winSize.width / 2, this.winSize.height / 2);
var shaderProgram = new cc.GLProgram();
shaderProgram.init("GrayScaleShader.vsh", "GrayScaleShader.fsh");
shaderProgram.addAttribute(cc.ATTRIBUTE_NAME_POSITION, cc.VERTEX_ATTRIB_POSITION);
shaderProgram.addAttribute(cc.ATTRIBUTE_NAME_TEX_COORD, cc.VERTEX_ATTRIB_TEX_COORDS);
shaderProgram.link();
shaderProgram.updateUniforms();
sprite.setShaderProgram(shaderProgram);
this.addChild(sprite);
And here is the shader vsh:
attribute vec4 a_position;
attribute vec2 a_texCoord;
#ifdef GL_ES
varying mediump vec2 v_texCoord;
#else
varying vec2 v_texCoord;
#endif
void main()
{
gl_Position = (CC_PMatrix * CC_MVMatrix) * a_position;
v_texCoord = a_texCoord;
}
and fsh:
#ifdef GL_ES
precision lowp float;
#endif
varying vec2 v_texCoord;
void main(void) {
vec4 normalColor = texture2D(CC_Texture0, v_texCoord).rgba;
float grayColor = dot(normalColor.rgb, vec3(0.299, 0.587, 0.114));
gl_FragColor = vec4(grayColor, grayColor, grayColor, normalColor.a);
}
This code works fine in Browser, I can see a gray sprite in the center of the screen.
But in Mac and iOS (JSBinding) the sprite is gray but the position of the sprite is in the right top of the screen (not in the center where it should be).
Not sure what is going wrong here, any advice will be appreciated, thanks :)
For some reason in browser works with:
gl_Position = (CC_PMatrix * CC_MVMatrix) * a_position;
but in native iOS you have to modify the vertex shader to:
gl_Position = CC_PMatrix * a_position;
Related
I'm trying to change texture colors inside the GLSL context - doing so before the beginning of the OpenGL pipeline is not an option.
I have tried the following approach:
Vertex Shader
attribute highp vec2 a_TexCoord;
uniform highp mat3 u_TextureMatrix;
varying highp vec2 v_TexCoord;
highp vec4 calculatePosition();
void main()
{
gl_Position = calculatePosition();
v_TexCoord = (u_TextureMatrix * vec3(a_TexCoord,1.0)).xy;
}
attribute highp vec2 a_Vertex;
uniform highp mat3 u_TransformMatrix;
uniform highp mat3 u_ProjectionMatrix;
highp vec4 calculatePosition() {
return vec4(u_ProjectionMatrix * u_TransformMatrix * vec3(a_Vertex.xy, 1.0), 1.0);
}
Fragment Shader
uniform lowp float u_Opacity;
lowp vec4 calculatePixel();
void main()
{
gl_FragColor = calculatePixel();
gl_FragColor.a *= u_Opacity;
}
varying mediump vec2 v_TexCoord;
uniform lowp vec4 u_Color;
uniform sampler2D u_Tex0;
lowp vec4 calculatePixel() {
vec4 tex = texture2D(u_Tex0, v_TexCoord);
tex.xyz -= (100.0 / 255.0);
if (tex.x < 0.0) { tex.x += 1.0; }
if (tex.y < 0.0) { tex.y += 1.0; }
if (tex.z < 0.0) { tex.z += 1.0; }
return tex * u_Color;
}
This code works EXCEPT for the cases when Interpolation is applied (GL_LINEAR_MIPMAP_LINEAR), when things start to look really bad because the filtering happens before the Fragment Shader so I don't get to change the colors PRIOR when it's reliable to do so.
I'm developing a game that has as requirement supporting really old hardware (from as early as 2008) and that's why I'm using really obsolete GLSL code (version 120 compatible).
Is there a way to change Texture colors anywhere in the pipeline BEFORE the rasterization kicks in?
I'm trying to implement a shader for grass in cocos2d-x. The shader works OK on texture loaded with Sprite::create() and it looks like this:
http://i.stack.imgur.com/Rv4rd.png
The problem is that if I'm using Sprite::createWithSpriteFrameName() and applying the same shader it looks like the offsets are wrong when calculating height also because it is moving at a larger degree, like it is using the height of the full texture from plist file:
http://i.stack.imgur.com/of6Ku.png
Here is the shader code:
VSH
attribute vec4 a_position;
attribute vec2 a_texCoord;
attribute vec4 a_color;
#ifdef GL_ES
varying lowp vec4 v_fragmentColor;
varying mediump vec2 v_texCoord;
#else
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
#endif
void main()
{
gl_Position = CC_PMatrix * a_position;
v_fragmentColor = a_color;
v_texCoord = a_texCoord;
}
FSH
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 v_texCoord;
uniform float speed;
uniform float bendFactor;
void main()
{
float height = 1.0 - v_texCoord.y;
float offset = pow(height, 2.5);
offset *= (sin(CC_Time[1] * speed) * bendFactor);
gl_FragColor = texture2D(CC_Texture0, fract(vec2(v_texCoord.x + offset, v_texCoord.y))).rgba;
}
If what is happening is not clear I can provide some videos. Thank you.
EDIT
Here is the code used to generate the grass sprite:
// Smaller grass
auto grass2 = Sprite::createWithSpriteFrameName("grass2.png");
grass2->setAnchorPoint(Vec2(0.5f, 0));
grass2->setPosition(Vec2(230, footer1->getContentSize().height * 0.25f));
// Apply "grass" shader
grass2->setGLProgramState(mat->getTechniqueByName("grass")->getPassByIndex(0)->getGLProgramState()->clone());
grass2->getGLProgramState()->setUniformFloat("speed", RandomHelper::random_real(0.5f, 3.0f));
grass2->getGLProgramState()->setUniformFloat("bendFactor", RandomHelper::random_real(0.1f, 0.2f));
It's hard to tell what's happening without seeing more of your code...
If I should guess I would say that the problem is related to trimming in TexturePacker.
If you set TrimMode=Trim the sprite is stripped from transparency. This makes the sprite smaller. Cocos2d-x also only renders the smaller portion of the sprite, compensating the difference between the original sprite and the trimmed sprite with an offset vector.
I propose that you either try not to trim the sprite or try polygon trimming.
The problem was with TexturePacker trimming but also with offsets in v_texCoord.
The solution was to calculate offsets in cocos2d-x and pass them to shader.
I calculated offsets using following code:
Rect grass2Offset(
grass2->getTextureRect().origin.x / grass2->getTexture()->getContentSize().width,
grass2->getTextureRect().origin.y / grass2->getTexture()->getContentSize().height,
grass2->getTextureRect().size.width / grass2->getTexture()->getContentSize().width,
grass2->getTextureRect().size.height / grass2->getTexture()->getContentSize().height
);
Next I pass the height offset and scale to shader as uniforms using:
grass2->getGLProgramState()->setUniformFloat("heightOffset", grass2Offset.origin.y);
grass2->getGLProgramState()->setUniformFloat("heightScale", 1 / grass2Offset.size.height);
Last, the shader is using the offset like this:
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 v_texCoord;
uniform float speed;
uniform float bendFactor;
uniform float heightOffset;
uniform float heightScale;
void main()
{
float height = 1.0 - (v_texCoord.y - heightOffset) * heightScale;
float offset = pow(height, 2.5);
offset *= (sin(CC_Time[1] * speed) * bendFactor);
gl_FragColor = texture2D(CC_Texture0, fract(vec2(v_texCoord.x + offset, v_texCoord.y))).rgba;
}
I'm very new to GLSL and started with a simple greyscale shade. I used the code of GamesFromScratch's tutorial:
vertexshader:
attribute vec4 a_position;
attribute vec4 a_color;
attribute vec2 a_texCoord0;
uniform mat4 u_projTrans;
varying vec4 v_color;
varying vec2 v_texCoords;
void main() {
v_color = a_color;
v_texCoords = a_texCoord0;
gl_Position = u_projTrans * a_position;
}
Fragmentshader:
#ifdef GL_ES
precision mediump float;
#endif
varying vec4 v_color;
varying vec2 v_texCoords;
uniform sampler2D u_texture;
uniform mat4 u_projTrans;
void main() {
vec3 color = texture2D(u_texture, v_texCoords).rgb;
float gray = (color.r + color.g + color.b) / 3.0;
vec3 grayscale = vec3(gray);
gl_FragColor = vec4(grayscale, 1.0);
}
The effect and the problem: Everything is rendered in grayscale only, but transparent parts of the textures become white. For example: A simple filled circle is usually drawn as a circle. Now its a circle within a white box. Next to the removed transparent parts also changes on the alpha are not visible.
The problem is in your fragment shader. You create a vec3 color imagine (r,g,b) then you set gl_FragColor to a vec4 (r,g,b,a). Use use the first three from grayscale and then set the "a" to a hard coded alpha value of 1, removing any transparency.
You could get the rgba from the sampler and use its alpha in the final vec4.
Also if you are looking for a more true grayscale conversion the general standard is
color = 0.299 * r + 0.587 * g + 0.114 * b
https://en.wikipedia.org/wiki/Grayscale
I think these changes will help you.
vec4 color = texture2D(u_texture, v_texCoords);
float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));
gl_FragColor = vec4(grayscale, color.a);
In my changes, I read color with alpha from texture and apply it to output.
this is my fragment shader
#ifdef GL_ES
#define LOWP lowp
precision mediump float;
#else
#define LOWP
#endif
varying LOWP vec4 v_color;
varying vec2 v_texCoords;
uniform sampler2D u_texture;
void main()
{
vec4 final_color = v_color * texture2D(u_texture, v_texCoords);
if (final_color.w != 0.0){
final_color = v_color;
}
gl_FragColor = final_color;
}
It looks like your original image is anti-aliased. Try just using the color of v_color with the alpha from the texture:
gl_FragColor.xyz = v_color.xyz;
gl_FragColor.w = texture2D(u_texture, v_texCoords).w;
if (final_color.w >= 0.0){
final_color.w = clamp(final_color.w, 0.0,1.0);
final_color.xyz = v_color.xyz;
}
gl_FragColor = final_color;
I figured out I can also use this but #GuyRT's way is more efficient
I´ve created a shader that is able to rotate an image about 180° and overlay it with a black gradient but now I want to create real transparency instead of using black as my background color.
This is what I got so far:
// Vertex Shader
uniform highp mat4 u_modelViewMatrix;
uniform highp mat4 u_projectionMatrix;
attribute highp vec4 a_position;
attribute lowp vec4 a_color;
attribute highp vec2 a_texcoord;
varying lowp vec4 v_color;
varying highp vec2 v_texCoord;
uniform int offset;
uniform int space;
uniform int vph;
void main()
{\
highp float h = float(offset)/float(vph);
highp float s = float(space)/1000.0;
highp vec4 pos = a_position;
pos.y = pos.y - (h + s);
gl_Position = (u_projectionMatrix * u_modelViewMatrix) * pos;
v_color = a_color;
v_texCoord = vec2(a_texcoord.x, 1.0 - a_texcoord.y);
}
// Fragment Shader
varying highp vec2 v_texCoord;
uniform sampler2D u_texture0;
uniform int gradient;
void main()
{
lowp vec3 w = vec3(1.0,1.0,1.0);
lowp vec3 b = vec3(0.0,0.0,0.0);
lowp vec3 mix = mix(b, w, (v_texCoord.y-(float(gradient)/10.0)));
gl_FragColor = texture2D(u_texture0,v_texCoord) * vec4(mix, 1.0);
}
replace black color with translucent color in your fragment shader:
void main()
{
lowp vec4 w = vec3(1.0,1.0,1.0,1.0);
lowp vec4 b = vec3(0.0,0.0,0.0,0.0);
lowp vec4 mix = mix(b, w, (v_texCoord.y-(float(gradient)/10.0)));
gl_FragColor = texture2D(u_texture0,v_texCoord) * mix;
}