Cocos2d-x shader is using invalid offset on texturepacker imported spriteframe - opengl

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;
}

Related

cocos2d-x position issue when using setShaderProgram

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;

GLSL Grayscale Shader removes transparency

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.

Is this a practical and enough performant shader for doing blur on mobile device

I am trying to implement Blur effect in my game on mobile devices using GLSL shader. I don't have any former experience with writing shaders. And I don't understand if my shader is enough good. Actually I have copyied the GLSL code from a tutorial and I don't know it this tutorial is for vivid demo or also can be used in practice. Here is the code of two pass blur shader that uses Gaussian weights (http://www.cocos2d-x.org/wiki/User_Tutorial-RenderTexture_Plus_Blur):
#ifdef GL_ES
precision mediump float;
#endif
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
uniform vec2 pixelSize;
uniform vec2 direction;
uniform int radius;
uniform float weights[64];
void main()
{
gl_FragColor = texture2D(CC_Texture0, v_texCoord)*weights[0];
for (int i = 1; i < radius; i++) {
vec2 offset = vec2(float(i)*pixelSize.x*direction.x, float(i)*pixelSize.y*direction.y);
gl_FragColor += texture2D(CC_Texture0, v_texCoord + offset)*weights[i];
gl_FragColor += texture2D(CC_Texture0, v_texCoord - offset)*weights[i];
}
}
I run this shader on each frame update (60 times in a sec) and my game framerate for only one pass drops down to 22 FPS on iPhone 5S (not a bad device). I think this is very-very strange. it seems it has not to much instruction. Why this is so heavy?
P.S. Blur radius is 50, step is 1.
Main reasons why your shader is heavy:
1: This two calculations: v_texCoord + offset and v_texCoord - offset. because the uv coordonates are computed in the fragment shader the texture data has to be loaded from memory on the spot causing cache miss.
What is a dependent texture read?
2: radius is way to large.
How to make it faster/better:
1: Calculate as much as possible in the vertex shader. Ideally if you calculate all the UV's in the vertex shader the GPU can move the texture memory in cache before calling fragment shaders, drastically improving performance.
2: reduce Radius to accommodate let's say 8-16 texture2D calls. This will probably not give you the result you are expecting, and to solve this you can have 2 textures, blurring texture A into B , then blur again B into texture A and so on, as mush as you need. This will give very good results, i remember crisys 1 used it for motion blur , but i can't find the paper.
3: eliminate those 64 uniforms, have all the data hardcoded in the shader. I know that this is not that nice but you will gain some extra performance.
4: If you carefully calculate the UV coordinates you can take great advantage of texture interpolation. Basically never sample a pixel on it's center, always sample in between pixels and the hardware will do and avrage of the near 4 pixels:
https://en.wikipedia.org/wiki/Bilinear_filtering
5: This line: precision mediump float; does everything have to be mediump? I would suggest to remove it and do some testing with lowp on as much as you can.
Edit:
For you shader, here is a simplified version of what you need to do:
Vertex shader:
attribute highp vec4 Position;
attribute mediump vec2 texture0UV;
varying mediump vec2 v_texCoord0;
varying mediump vec2 v_texCoord1;
varying mediump vec2 v_texCoord2;
varying mediump vec2 v_texCoord3;
varying mediump vec2 v_texCoord5;
uniform mediump vec2 texture_size;
void main()
{
gl_Position = Position;
vec2 pixel_size = vec2(1.0) / texture_size;
vec2 offset;
v_texCoord0 = texture0UV;
v_texCoord1 = texture0UV + vec2(-1.0,0.0) / texture_size + pixel_size * 0.5;
v_texCoord2 = texture0UV + vec2(0.0,-1.0) / texture_size + pixel_size * 0.5;
v_texCoord3 = texture0UV + vec2(1.0,0.0) / texture_size - pixel_size * 0.5;
v_texCoord4 = texture0UV + vec2(0.0,1.0) / texture_size - pixel_size * 0.5;
}
The last operation pixel_size * 0.5 is required to take maximum advantage of linear interpolation. In this example the position you pick for sampling are trivial but there is an entire discussion on how you should pick your sampling positions that is way out of the scope of this question.
Fragment shader:
varying mediump vec2 v_texCoord0;
varying mediump vec2 v_texCoord1;
varying mediump vec2 v_texCoord2;
varying mediump vec2 v_texCoord3;
varying mediump vec2 v_texCoord5;
uniform lowp sampler2D CC_Texture0;
void main()
{
mediump vec4 final_color = vec4(0.0);
final_color += texture2D(CC_Texture0,v_texCoord0);
final_color += texture2D(CC_Texture0,v_texCoord1);
final_color += texture2D(CC_Texture0,v_texCoord2);
final_color += texture2D(CC_Texture0,v_texCoord3);
final_color += texture2D(CC_Texture0,v_texCoord4);
gl_FragColor = final_color / 5.0;//weights have to go, use fixed values instead, in this case it's 1/5 for each sample
}
For this to look good you need to blur the texture multiple times, even if you blur the texture 2 times you should see a notable difference.
To speed up you can:
Make radius a const to allow shader compiler to unroll the loop
Precompute pixelSize * direction
Decrease radius, I think 50 is too big for mobile device

GLSL Texture Size

I have a problem with my fragment shader.
I want to get the size of a texture (which is loaded from an image).
I know that it is possible to use textureSize(sampler) to get an ivec2 which contains the texture size. But i don't know why this isn't working (it doesn't compile):
#version 120
uniform sampler2D tex;
float textureSize;
float texelSize;
void main()
{
textureSize = textureSize(tex).x;//first line
//textureSize = 512.0;//if i set the above line as comment and use this one the shader compiles.
texelSize = 1.0 / textureSize;
vec4 color = texture2D(tex,gl_TexCoord[0].st);
gl_FragColor = color * gl_Color;
}
Th problem was that my GLSL version was to low (implemented in 1.30) and that i was missing a parameter.
Here the working version:
#version 130
uniform sampler2D tex;
float textureSize;
float texelSize;
void main()
{
ivec2 textureSize2d = textureSize(tex,0);
textureSize = float(textureSize2d.x);
texelSize = 1.0 / textureSize;
vec4 color = texture2D(tex,gl_TexCoord[0].st);
gl_FragColor = color * gl_Color;
}

2d terrain generation using shader

I am trying to render a random terrain using shader scripts
Create function..
tex = new Texture(Gdx.files.internal("ground.png"));
top = new Texture(Gdx.files.internal("top.png"));
ShaderProgram.pedantic = false;
shader = new ShaderProgram(VERT, FRAG);
shader.begin();
shader.setUniformi("u_top", 1);
shader.end();
top.bind(1);
Gdx.gl.glActiveTexture(GL10.GL_TEXTURE0);
batch = new SpriteBatch(1000, shader);
batch.setShader(shader);
Render function
batch.begin();
for (int i = 0; i < 4; i++)
batch.draw(tex, i * 256 * Initiate.getScale(), 0,
256 * Initiate.getScale(), 256 * Initiate.getScale());
batch.end();
Vertex shader
attribute vec4 a_position;
attribute vec4 a_color;
attribute vec2 a_texCoord0;
uniform mat4 u_projTrans;
varying vec4 vColor;
varying vec2 vTexCoord;
void main()
{
vTexCoord = a_texCoord0;
gl_Position = u_projTrans * a_position;
}
Fragment shader
#ifdef GL_ES
#define LOWP lowp
precision mediump float;
#else
#define LOWP
#endif
varying LOWP vec4 vColor;
varying vec2 vTexCoord;
uniform sampler2D u_texture1;
uniform sampler2D u_texture2;
void main()
vec4 texColor ;
//calculate top vertex
float clamp = 0.5 + slope*(sin(vTexCoord0.x*mfrq)/mfrq+sin(vTexCoord0.x*frq)/frq+sin(vTexCoord0.x*nfrq)/nfrq);
//if larger the draw texture
if(vTexCoord0.y > clamp){
texColor = texture2D(u_texture1, vTexCoord1);
}
// else map coordinate for top texture and draw
else{
float tempy = 16.0*(vTexCoord0.y + 0.0625- clamp);
texColor = texture2D(u_texture2, vec2 (vTexCoord1.x,tempy));
}
gl_FragColor = texColor;
Output
So the question is..
How do a add a texture at the top of this terrain?
Is there any simple way of redering such a terrain?
You can do this in fragment shader by calculating two parameters - distance along the terrain surface, and depth perpendicular to surface, this can be done through attributes or calculated in vertex shader, depending on how you generate you terrain, then just sample your texture based on those two coordinates, with tiling along the first coordinate.
Distance along the surface is just a sum lengths of edges that form surface.
Depth can be calculated as distance from vertex to surface edge, and interpolation will give you approximate depth in the whole polygon. Quads should give you better interpolation then triangles.