Mesh颜色未渲染:SpriteBatch绘制箭头颜色不生效问题排查
问题:SpriteBatch绘制箭头时顶点颜色不生效,箭头显示黑色
我用以下代码通过SpriteBatch绘制箭头,但Shader没有使用MeshBuilder API设置的颜色。顶点颜色属性配置看似正确,但箭头始终显示黑色而非设置的红色。自定义箭头构建器用于硬编码杆直径和锥高,避免从长度推导以实现不同宽度的箭头。主渲染循环通过Stage执行并绘制这些Actor,期望Mesh顶点颜色属性能从MeshBuilder的默认颜色生效,目前未达预期,求问遗漏了什么?
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.Mesh; import com.badlogic.gdx.graphics.VertexAttributes.Usage; import com.badlogic.gdx.graphics.g2d.Batch; import com.badlogic.gdx.graphics.g3d.utils.MeshBuilder; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.graphics.g3d.utils.MeshPartBuilder; import com.badlogic.gdx.graphics.g3d.utils.shapebuilders.BaseShapeBuilder; import com.badlogic.gdx.graphics.g3d.utils.shapebuilders.ConeShapeBuilder; import com.badlogic.gdx.graphics.g3d.utils.shapebuilders.CylinderShapeBuilder; import com.badlogic.gdx.math.Matrix4; import com.badlogic.gdx.math.Vector3; public class InvasionPointerActor extends Actor { private final Mesh arrowMesh; public InvasionPointerActor(Vector3 start, Vector3 end) { MeshBuilder builder = new MeshBuilder(); builder.setColor(Color.RED); builder.begin(Usage.Position | Usage.Normal | Usage.ColorUnpacked, GL20.GL_TRIANGLES); CustomArrowShapeBuilder abuilder = new CustomArrowShapeBuilder(); abuilder.build(builder, start.x, start.y, 0, end.x, end.y, 0, 0.05f, 0.5f, 10); arrowMesh = builder.end(); } @Override public void draw(Batch batch, float parentAlpha) { arrowMesh.render(batch.getShader(), GL20.GL_TRIANGLES); } private class CustomArrowShapeBuilder extends BaseShapeBuilder { public void build(MeshPartBuilder builder, float x1, float y1, float z1, float x2, float y2, float z2, float capLength, float stemThickness, int divisions) { Vector3 begin = obtainV3().set(x1, y1, z1), end = obtainV3().set(x2, y2, z2); float length = begin.dst(end); float coneHeight = 300 * capLength; float coneDiameter = 2 * (float) (coneHeight * Math.sqrt(1f / 3)); float stemLength = length - coneHeight; float stemDiameter = 7; Vector3 up = obtainV3().set(end).sub(begin).nor(); Vector3 forward = obtainV3().set(up).crs(Vector3.Z); if (forward.isZero()) { forward.set(Vector3.X); } forward.crs(up).nor(); Vector3 left = obtainV3().set(up).crs(forward).nor(); Vector3 direction = obtainV3().set(end).sub(begin).nor(); Matrix4 userTransform = builder.getVertexTransform(obtainM4()); Matrix4 transform = obtainM4(); float[] val = transform.val; val[Matrix4.M00] = left.x; val[Matrix4.M01] = up.x; val[Matrix4.M02] = forward.x; val[Matrix4.M10] = left.y; val[Matrix4.M11] = up.y; val[Matrix4.M12] = forward.y; val[Matrix4.M20] = left.z; val[Matrix4.M21] = up.z; val[Matrix4.M22] = forward.z; Matrix4 temp = obtainM4(); transform.setTranslation(obtainV3().set(direction).scl(stemLength / 2).add(x1, y1, z1)); builder.setVertexTransform(temp.set(transform).mul(userTransform)); CylinderShapeBuilder.build(builder, stemDiameter, stemLength, stemDiameter, divisions); transform.setTranslation(obtainV3().set(direction).scl(stemLength).add(x1, y1, z1)); builder.setVertexTransform(temp.set(transform).mul(userTransform)); ConeShapeBuilder.build(builder, coneDiameter, coneHeight, coneDiameter, divisions); builder.setVertexTransform(userTransform); freeAll(); } } }
问题原因
- SpriteBatch默认Shader不支持顶点颜色:SpriteBatch的默认Shader是为2D纹理绘制设计的,它不会读取Mesh的顶点颜色属性,只会使用
Batch.setColor()设置的颜色或纹理本身的颜色,所以即使Mesh中存储了红色顶点数据,默认Shader也不会解析使用。 - 直接调用Mesh.render()脱离SpriteBatch状态:SpriteBatch内部维护了一套渲染状态(如纹理绑定、矩阵变换、Shader参数),直接调用
Mesh.render()会绕过这些状态,且你的Mesh包含了Normal属性(SpriteBatch默认Shader不需要),进一步导致渲染异常。
解决方法
方法一:使用支持顶点颜色的自定义Shader
编写适配SpriteBatch的自定义Shader,让它能读取并使用顶点颜色:
顶点着色器:
attribute vec4 a_position; attribute vec4 a_color; uniform mat4 u_projTrans; varying vec4 v_color; void main() { v_color = a_color; gl_Position = u_projTrans * a_position; }
片段着色器:
#ifdef GL_ES precision mediump float; #endif varying vec4 v_color; void main() { gl_FragColor = v_color; }
然后在初始化SpriteBatch时替换默认Shader:
SpriteBatch batch = new SpriteBatch(); String vertexShader = "上述顶点着色器代码"; String fragmentShader = "上述片段着色器代码"; ShaderProgram customShader = new ShaderProgram(vertexShader, fragmentShader); if (!customShader.isCompiled()) { Gdx.app.error("Shader", customShader.getLog()); } batch.setShader(customShader);
方法二:改用ModelBatch渲染(推荐)
因为你用3D的MeshBuilder创建了包含法线、顶点颜色的Mesh,更适合用3D渲染管线的ModelBatch来处理,它默认支持顶点颜色:
- 初始化ModelBatch和相机:
private ModelBatch modelBatch; private OrthographicCamera camera; // 在Actor初始化或游戏create方法中 modelBatch = new ModelBatch(); camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); camera.update();
- 修改draw方法:
@Override public void draw(Batch batch, float parentAlpha) { batch.end(); // 先结束SpriteBatch的当前渲染批次 modelBatch.begin(camera); modelBatch.render(arrowMesh, GL20.GL_TRIANGLES); modelBatch.end(); batch.begin(); // 恢复SpriteBatch的渲染状态 }
方法三:放弃顶点颜色,用SpriteBatch的setColor()
如果只需要统一的红色箭头,可以去掉Mesh的ColorUnpacked和Normal属性,直接用Batch.setColor()控制颜色:
- 修改MeshBuilder初始化:
builder.begin(Usage.Position, GL20.GL_TRIANGLES); // 只保留位置属性
- 修改draw方法:
@Override public void draw(Batch batch, float parentAlpha) { batch.setColor(Color.RED); // 注意:SpriteBatch的Mesh渲染需要纹理坐标,若没有则仍需配合自定义Shader,所以此方法优先级低于前两种 arrowMesh.render(batch.getShader(), GL20.GL_TRIANGLES); }
内容的提问来源于stack exchange,提问作者diesel10
相关产品推荐
相关产品推荐

