我正在尝试对GLSL中设置的mandelbrot进行编码。但这似乎是由于GLSL内部分支的限制所致,无法正常工作。这是我的代码:
passthrough.vsh:
attribute vec4 a_color;
attribute vec3 a_position;
attribute vec2 a_texCoord0;
uniform mat4 u_projTrans;
varying vec4 v_color;
varying vec2 v_texCoord0;
void main() {
v_color = a_color;
v_texCoord0 = a_texCoord0;
gl_Position = u_projTrans * vec4(a_position, 1);
}
mandelbrot.fsh:
varying vec4 v_color;
varying vec2 v_texCoord0;
uniform sampler2D u_sampler2D;
uniform int u_width;
uniform int u_height;
uniform float u_realCenter;
uniform float u_imagCenter;
uniform float u_realSpan;
uniform float u_imagSpan;
int maxIterations = 255;
float rand(vec2 c0);
vec2 squareComplex(vec2 z);
vec2 coordToComplexNumber(vec2 coord);
float getRed(int stability);
float getGreen(int stability);
float getBlue(int stability);
void main()
{
vec2 c = coordToComplexNumber(gl_FragCoord.xy);
vec2 z = vec2(0, 0);
int stability = 0;
int attempts = 0;
for (int i = 0; i < maxIterations && z.x * z.x + z.y * z.y < 4.0; i++) {
z = squareComplex(z) + c;
stability = stability + 1;
}
gl_FragColor = vec4(getRed(stability), getGreen(stability), getBlue(stability), 1);
}
vec2 squareComplex(vec2 z)
{
return vec2(z.x * z.x - z.y * z.y, 2.0 * z.x * z.y);
}
vec2 coordToComplexNumber(vec2 coord)
{
float lowestReal = u_realCenter - u_realSpan / 2.0;
float lowestImag = u_imagCenter - u_imagSpan / 2.0;
float portionX = coord.x / float(u_width);
float portionY = coord.y / float(u_height);
float mappedX = lowestReal + portionX * u_realSpan;
float mappedY = lowestImag + portionY * u_imagSpan;
return vec2(mappedX, mappedY);
}
float getRed(int stability)
{
return float(stability) / float(maxIterations) * 255.0;
}
float getGreen(int stability)
{
return mod(float(stability), float(maxIterations)) * 255.0;
}
float getBlue(int stability)
{
return 0.0;
}
问题似乎在这里发生:
for (int i = 0; i < maxIterations && z.x * z.x + z.y * z.y <= 4.0; i++) {
z = squareComplex(z) + c;
stability = stability + 1;
}
使用下面的代码进行测试表明,此循环每次只运行1次迭代,然后退出。
gl_FragColor = vec4(stability == 1 ? 1 : 0, 0, 0, 1);
最终将所有内容都涂成红色。
我还尝试在循环内使用break语句,而不是在for循环条件内使用&& z.x * z.x + z.y * z.y <= 4.0
,如下所示:
if (z.x * z.x + z.y * z.y > 4.0) break;
这似乎总是错误的。因此总是退出。我已经了解了GLSL中分支的限制。因此这可能是这两种情况的原因,但是我不知道如何在不分支的情况下在GLSL中编码Mandelbrot集,我找不到任何解决方案,也找不到在GLSL中使分支工作的方法。
[我在Linux上的Intel HD笔记本电脑,Windows上的NVidia笔记本电脑和Samsung Galaxy S9上测试了此代码,但它们在其中任何一个上均不起作用。
此代码正在Java LibGDX中实现。
也许与该错误有关:
LwjglGraphics: created OpenGL 3.2+ core profile (GLES 3.0) context. This is experimental!
这是从我的DesktopLauncherCode:
package mypackagename.desktop
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import mypackagename.Mandelbrot;
public class DesktopLauncher {
public static void main (String[] arg) {
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.vSyncEnabled = true;
config.useGL30 = true;
config.title = "GLSL Shaders";
config.width = 1280;
config.height = 720;
new LwjglApplication(new Mandelbrot(), config);
}
}
这是我的主要代码:
package mypackagename;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
public class Mandelbrot extends ApplicationAdapter {
private SpriteBatch batch;
private ShaderProgram shader;
private Texture texture;
@Override
public void create () {
batch = new SpriteBatch();
ShaderProgram.pedantic = false;
shader = new ShaderProgram(
Gdx.files.internal("shaders/mandelbrot3.vsh"),
Gdx.files.internal("shaders/mandelbrot3.fsh")
);
System.out.println(shader.isCompiled() ? "shader compiled, yay" : shader.getLog());
batch.setShader(shader);
shader.setUniformf("u_realCenter", 0);
shader.setUniformf("u_imagCenter", 0);
shader.setUniformf("u_realSpan", 6);
shader.setUniformf("u_imagSpan", 6);
}
@Override
public void render () {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.draw(texture, 0, 0);
batch.end();
}
@Override
public void dispose () {
batch.dispose();
shader.dispose();
}
@Override
public void resize(int width, int height) {
super.resize(width, height);
texture = new Texture(getPixmapRectangle(width, height, Color.BLUE));
shader.setUniformi("u_width", width);
shader.setUniformi("u_height", height);
}
public static Pixmap getPixmapRectangle(int width, int height, Color color) {
Pixmap pixmap = new Pixmap(width, height, Pixmap.Format.RGBA8888);
pixmap.setColor(color);
pixmap.fillRectangle(0, 0, pixmap.getWidth(), pixmap.getHeight());
return pixmap;
}
}
在OpenGL ES 2.0中,for
循环有几个限制。参见OpenGL ES Shading Language 1.00 Specification - Appendix A: Limitations for ES 2.0:
4控制流程[...]支持for循环,但具有以下限制:[...]条件具有形式
loop_index relational_operator constant_expression
其中relational_operator为以下之一:>> = <<= ==或!=
导致片段着色器无法成功编译。
但是可以在break
循环中使用for
语句。例如:
void main()
{
// [...]
for (int i = 0; i < maxIterations; i++) {
if (z.x * z.x + z.y * z.y >= 4.0)
break;
z = squareComplex(z) + c;
stability = stability + 1;
}
// [...]
}