You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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来处理,它默认支持顶点颜色:

  1. 初始化ModelBatch和相机:
private ModelBatch modelBatch;
private OrthographicCamera camera;

// 在Actor初始化或游戏create方法中
modelBatch = new ModelBatch();
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
camera.update();
  1. 修改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()控制颜色:

  1. 修改MeshBuilder初始化:
builder.begin(Usage.Position, GL20.GL_TRIANGLES); // 只保留位置属性
  1. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 04:18:13