I want to replace every white pixel in an image by black, but keep everything else unchanged
Window {
id: window;
width: 800;
height: 600;
visible: true;
Image {
id: sourceImage
anchors.centerIn: parent
source: "qrc:/source.png"
}
Item {
anchors.fill: sourceImage
layer.enabled: true
layer.samplerName: "maskSource"
layer.effect: ShaderEffect {
fragmentShader: "
uniform lowp sampler2D maskSource;
uniform lowp float qt_Opacity;
varying highp vec2 qt_TexCoord0;
void main() {
vec4 pixel = texture2D(maskSource, qt_TexCoord0);
if (pixel == vec4(1.0, 1.0, 1.0, pixel.a)) {
pixel = vec4(0.0, 0.0, 0.0, pixel.a);
}
gl_FragColor = vec4(pixel.r, pixel.g, pixel.b, pixel.a);
}"
}
}
}
I can't manage to make it work
I found a solution to my problem :
ShaderEffect {
property variant src: sourceImage
vertexShader: "
uniform highp mat4 qt_Matrix;
attribute highp vec4 qt_Vertex;
attribute highp vec2 qt_MultiTexCoord0;
varying highp vec2 coord;
void main() {
coord = qt_MultiTexCoord0;
gl_Position = qt_Matrix * qt_Vertex;
}"
fragmentShader: "
uniform lowp sampler2D src;
uniform lowp float qt_Opacity;
varying highp vec2 coord;
void main() {
vec4 pixel = texture2D(src, coord);
if (pixel.r > 0.2 && pixel.g > 0.2 && pixel.b > 0.2) {
pixel.rgb = vec3(0.0, 0.0, 0.0);
}
gl_FragColor = pixel;
}"
}
The values I use to detect whites (0.2,0.2,0.2) work for my images, but they would probably not work for every cases. I don't know if this is the best that can be done.
Maybe you should use a image preprocessing algorithm in Python or C++ for example on your image ? And then just load it in your QML application.
QML is aimed to be used for UI part, and you have to avoid as much as possible to do such things like image processing in QML.
Related
I'm trying to make a GLSL shader. The shader should display a red outline around my character. The shader works, but on some specific characters there are red lines, which I don't know how to fix
This is how my shader fragmetn looks:
uniform mat4 u_Color;
varying vec2 v_TexCoord;
varying vec2 v_TexCoord2;
uniform sampler2D u_Tex0;
const float ALPHA_TOLERANCE = 0.01;
void main()
{
vec4 baseColor = texture2D(u_Tex0, v_TexCoord);
vec4 texcolor = texture2D(u_Tex0, v_TexCoord2);
if(texcolor.r > 0.9) {
baseColor *= texcolor.g > 0.9 ? u_Color[0] : u_Color[1];
} else if(texcolor.g > 0.9) {
baseColor *= u_Color[2];
} else if(texcolor.b > 0.9) {
baseColor *= u_Color[3];
}
vec4 pixel1 = texture2D(u_Tex0, vec2(v_TexCoord.x + 0.001, v_TexCoord.y));
vec4 pixel2 = texture2D(u_Tex0, vec2(v_TexCoord.x - 0.001, v_TexCoord.y));
vec4 pixel3 = texture2D(u_Tex0, vec2(v_TexCoord.x, v_TexCoord.y + 0.001));
vec4 pixel4 = texture2D(u_Tex0, vec2(v_TexCoord.x, v_TexCoord.y - 0.001));
bool neighbourColor = pixel4.a > ALPHA_TOLERANCE || pixel3.a > ALPHA_TOLERANCE || pixel2.a > ALPHA_TOLERANCE || pixel1.a > ALPHA_TOLERANCE;
if (baseColor.a < ALPHA_TOLERANCE && neighbourColor) {
baseColor.rgb = vec3(1.0, 0.0, 0.0);
baseColor.a = 0.7;
}
gl_FragColor = baseColor;
if(gl_FragColor.a < 0.01) discard;
}
This is how my vertex shader looks:
attribute vec2 a_Vertex;
attribute vec2 a_TexCoord;
uniform mat3 u_TextureMatrix;
varying vec2 v_TexCoord;
varying vec2 v_TexCoord2;
uniform mat3 u_TransformMatrix;
uniform mat3 u_ProjectionMatrix;
uniform vec2 u_Offset;
void main()
{
gl_Position = vec4((u_ProjectionMatrix * u_TransformMatrix * vec3(a_Vertex.xy, 1.0)).xy, 1.0, 1.0);
v_TexCoord = (u_TextureMatrix * vec3(a_TexCoord,1.0)).xy;
v_TexCoord2 = (u_TextureMatrix * vec3(a_TexCoord + u_Offset,1.0)).xy;
}
The shader works like this: it checks if the color is transparent, if it is, then it checks if a neighboring pixel has a real "color", if it is then it's replaced with red. But for some reason there are some random red lines outside of my character. There isn't any "real" colors next to those lines, so I don't understand why it's making those pixels red.
Example of a bugged character
I have the following fragment shader to draw a grid
#version 450 core
layout(location = 0) in vec2 position;
layout(location = 0) out vec4 fragColor;
layout(binding = 0) uniform ubo
{
mat4 uCameraView;
vec4 uGridColor;
float uTileSize;
float uGridBorderSize;
};
void main()
{
vec2 uv = mod(position, uTileSize);
vec2 border = mod(uv + (uGridBorderSize / 2.0), uTileSize);
border -= mod(uv - (uGridBorderSize / 2.0), uTileSize);
if (length(border) > uTileSize - uGridBorderSize)
{
fragColor = uGridColor;
}
else
{
fragColor = vec4(0.0);
}
}
This works fine until I change the zoom, the issue appears when the camera gets far away and the uGridBorderSize is smaller than a pixel in the screen, then I get this ugly effect, where lines appear and disappear when the zoom changes.
So I wonder, is it possible to apply antialising to this lines so they appear consistently?
In the end I found this fragment shader that draws antialiased grid lines
void main()
{
// https://madebyevan.com/shaders/grid/
vec2 uv = fragCoord / uTileSize;
vec2 grid = abs(fract(uv - 0.5) - 0.5) / fwidth(uv);
float line = (min(grid.x, grid.y) * uGridBorderSize) / uScaleFactor;
float color = 1.0 - min(line, 1.0);
fragColor = uGridColor * color;
}
I want to scale the textures so they can be centered and scaled so they cover the space without distorting the image. I tried some things but I don't seem to get it to work with the uniforms provided by pixi-js. I'm still a noob at GLSL /pixi.
Here is a codepen link with the problem: https://codepen.io/sergiulucutar/pen/JjKXKKw?editors=0010
const app = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
antialias: true,
resizeTo: window,
transparent: true
});
document.body.appendChild(app.view);
//Image
const vertexShader = `
precision mediump float;
attribute vec2 aVertexPosition;
attribute vec2 aSize;
uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
varying vec2 vUvs;
uniform vec2 uSize;
void main() {
vUvs = vec2(aVertexPosition.x / uSize.x, aVertexPosition.y / uSize.y);
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
}
`;
const fragmentShader = `
precision mediump float;
varying vec2 vUvs;
uniform sampler2D uTexture;
void main() {
gl_FragColor = texture2D(uTexture, vUvs);
}
`;
const width = 1400;
const height = 300;
const uniforms = {
uTexture: PIXI.Texture.from(imageUrl),
uSize: [width, height],
uTime: 0
};
const shader = PIXI.Shader.from(vertexShader, fragmentShader, uniforms);
const mesh = new PIXI.Mesh(new PIXI.PlaneGeometry(width, height, 32, 32), shader);
mesh.position.y = 100;
app.stage.addChild(mesh);
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?
Me and a friend are developing an editor (CAD-like) to use in our future game.
We are using the Qt framework and OpenGL.
The problem we are encountering is that on his laptop with an integrated nVidia card, the shading is working as expected and renders well. On my laptop with an integrated ATI card, as well as on my desktop with Radeon HD5850, the phong lighting is behaving slightly differently. There are more bright spots and dark spots and the image doesn't look good. Also, we are using a toon shader to draw a silhouette around the edges and to limit the amount of shades a color can have.
The toon shader uses 2-pass rendering - first pass renders the object in black, slightly larger than original (shifting each vertex in its normals direction slightly) to make the silhouette and then the second pass renders the object normally (only limiting the shade spectrum, so it looks more comic-like).
The images are of the same thing on our 2 computers. The first difference I mentioned above, the second is that the silhouette is stretched out as it should be on my friends computer, so it makes an even silhouette around the object, but is moved slightly up on my computer, making a thick line on the top and no line on the bottom.
The other thing is the phong lighting, illuminating the cube within which the object is edited. Again, rendering well on my friends computer, but being almost all-black or all-white on mine.
First image (nVidia card):
Second image (ATI cards):
I understand that the code is long and maybe the problem lays in some Qt settings, not in the shaders, but if you see anything that strikes you as bad practice, please answer.
Code for the phong shading follows
#version 400
in vec4 aVertex;
in vec4 aNormal;
in vec2 aTexCoord;
uniform mat4 uPVM;
uniform mat4 uViewModel;
uniform mat4 uNormal;
uniform int uLightsOn;
out vec2 vTexCoord;
out vec3 vNormal;
flat out vec3 mEye;
flat out vec3 mLightDirection;
flat out vec4 mAxisColor;
void main(void)
{
if(uLightsOn == 1) {
mEye = (uViewModel * aVertex).xyz;
mLightDirection = vec4(2.0,-2.0,1.0,0.0).xyz;
vNormal = (uNormal * aNormal).xyz;
}
gl_Position = uPVM * aVertex;
vTexCoord = aTexCoord;
mAxisColor = aNormal;
}
The phong fragment shader :
#version 400
uniform sampler2D uTexture0;
uniform int uLightsOn;
uniform vec3 uHighlightColor;
uniform int uTextured;
uniform int uAxisRender;
in vec2 vTexCoord;
in vec3 vNormal;
flat in vec3 mEye;
flat in vec3 mLightDirection;
out vec4 fragColor;
flat in vec4 mAxisColor;
struct TMaterial {
vec4 diffuse;
vec4 ambient;
vec4 specular;
float shininess;
};
TMaterial material;
void setup() {
// setupMaterials
material.ambient = vec4(0.4);
material.diffuse = vec4(0.9);
material.specular = vec4(0.0);
material.shininess = 0.3;
}
void main(void)
{
setup();
vec3 finalHighlightColor = uHighlightColor;
if(finalHighlightColor.x <= 0.0) finalHighlightColor.x = 0.1;
if(finalHighlightColor.y <= 0.0) finalHighlightColor.y = 0.1;
if(finalHighlightColor.z <= 0.0) finalHighlightColor.z = 0.1;
if(uLightsOn == 0) {
if(uAxisRender == 1) fragColor = mAxisColor;
else fragColor = vec4(finalHighlightColor,1.0);
return;
}
vec4 diffuse;
vec4 spec = vec4(0.0);
vec4 ambient;
vec3 L = normalize(mLightDirection - mEye);
vec3 E = normalize(-mEye);
vec3 R = normalize(reflect(-L,vNormal));
ambient = material.ambient;
float intens = max(dot(vNormal,L), 0.0);
diffuse = clamp( material.diffuse * intens , 0.0, 1.0 );
if(intens > 0.0) spec = clamp ( material.specular * pow(max(dot(R,E),0.0),material.shininess) , 0.0, 1.0 );
if(uTextured == 1) fragColor = (ambient + diffuse + spec) * texture(uTexture0,vTexCoord);
else fragColor = (ambient + diffuse + spec) * vec4(finalHighlightColor,1.0);
}
And the toon shaders :
#version 400
in vec4 aVertex;
in vec4 aNormal;
in vec2 aTexCoord;
uniform mat4 uPV;
uniform mat4 uM;
uniform mat4 uN;
uniform vec3 uLightPosition;
uniform vec3 uCameraPosition;
uniform int uSilhouetteMode;
uniform float uOffset;
// if this uniform is passed, all the toon rendering is going off and only simple axis are rendered
// last data in aNormal are colors of those axis if everything was ser properly.
uniform int uAxisRendering;
flat out vec4 fAxisColor;
out vec4 vNormal;
out vec2 vTexCoord;
out vec3 vDirectionToCamera;
out vec3 vDirectionToLight;
void silhouetteMode() {
gl_Position = uPV * uM * vec4(aVertex.xyz + aNormal.xyz * uOffset,1.0f);
}
void toonMode() {
vec4 worldPosition = uM * aVertex;
vDirectionToCamera = uCameraPosition - worldPosition.xyz;
vDirectionToLight = uLightPosition - worldPosition.xyz;
vNormal = uN * aNormal;
gl_Position = uPV * worldPosition;
}
void axisMode() {
fAxisColor = aNormal;
gl_Position = uPV * uM * aVertex;
}
void main(void)
{
vTexCoord = aTexCoord;
if(uSilhouetteMode == 1) {
silhouetteMode();
} else {
if(uAxisRendering == 1) axisMode();
else toonMode();
}
}
and the fragment shader
#version 400
uniform sampler2D uTexture;
uniform vec3 uBaseColor;
uniform float uNumShades;
uniform int uSilhouetteMode;
uniform int uAxisRendering;
flat in vec4 fAxisColor;
in vec4 vNormal;
in vec2 vTexCoord;
in vec3 vDirectionToCamera;
in vec3 vDirectionToLight;
out vec4 outFragColor;
void main(void)
{
if(uSilhouetteMode == 1) {
outFragColor = vec4(uBaseColor,1.0);
return;
}
if(uAxisRendering == 1) {
outFragColor = fAxisColor;
return;
}
float l_ambient = 0.1;
float l_diffuse = clamp(dot(vDirectionToLight,vNormal.xyz),0.0,1.0);
float l_specular;
vec3 halfVector = normalize(vDirectionToCamera + vDirectionToLight);
if(dot(vDirectionToLight,vNormal.xyz) > 0.0) {
l_specular = pow(clamp(dot(halfVector,vNormal.xyz),0.0,1.0),64.0);
} else {
l_specular = 0.0;
}
float intensity = l_ambient + l_diffuse + l_specular;
float shadeIntesity = ceil(intensity * uNumShades)/ uNumShades;
outFragColor = vec4(texture(uTexture,vTexCoord).xyz * shadeIntesity * uBaseColor,1.0);
}
And finally, our OpenGLWindow initialization (in Qt)
OpenGLWindow::OpenGLWindow(QWindow *parent) :
QWindow(parent),m_animating(false), m_initialized(false), m_animationTimer(NULL)
{
setSurfaceType(QWindow::OpenGLSurface);
QSurfaceFormat format;
format.setDepthBufferSize( 24 );
format.setMajorVersion( 4 );
format.setMinorVersion( 0 );
format.setSamples( 4 );
format.setProfile( QSurfaceFormat::CoreProfile );
setFormat( format );
create();
if(!m_context) {
m_context = new QOpenGLContext(this);
m_context->setFormat(requestedFormat());
m_context->create();
m_context->makeCurrent(this);
initializeOpenGLFunctions();
}
m_animationTimer = new QTimer(this);
connect(m_animationTimer, SIGNAL(timeout()), this, SLOT(renderer()));
m_animationTimer->setInterval(16);
}
To my eyes the nVidia image seems to be using alpha whereas the AMD one is not. I also can't see a
format.setAlpha(true);
in your Qt setup so may be that.