在JavaFX中,如何为网格的背面着色?

问题描述 投票:1回答:1

我正在使用this answer中讨论的相同技术在JavaFX中创建3D图,并使用MeshView和PhongMaterial提供颜色。但是,只有材料的顶面才是彩色的,并且如果用户旋转相机从下面观看,则无法确定绘图的形状,因为它全是黑色的。

我的问题:

  1. 有没有办法设置网格反面的材质?
  2. 如果没有,有没有很好的方法来“伪造”它?我可以想象在完全相同的位置上下颠倒地创建一个新的网格会导致渲染问题。最好的方法是这样做,但是应用很小的偏移量,以使两个网格不完全重叠?

Edit:我在下面提供了一些示例代码,该代码已从我的真实代码中删减,但包含足以说明问题的代码。默认情况下,它显示网格的顶部,在此示例中为红色。如果更改显示为new Rotate(-30, Rotate.X_AXIS)的线,以使角度变为+30而不是-30,它将旋转相机以显示网格的底面,您将看到该底面为黑色。

package test;

import javafx.application.Application;
import javafx.scene.DepthTest;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.SubScene;
import javafx.scene.image.Image;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.CullFace;
import javafx.scene.shape.DrawMode;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.TriangleMesh;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;

public class TestApp extends Application {

  public static void main(String[] args) {
    launch(args);
  }

  @Override
  public void start(Stage stage) throws Exception {
    TestPlot tp = new TestPlot();
    tp.setPrefSize(600, 400);
    Scene scene = new Scene(tp);
    stage.setScene(scene);
    stage.show();
  }

  class TestPlot extends Region {
    private final PerspectiveCamera camera = new PerspectiveCamera(true);
    private double[][] data = new double[500][500];
    private final StackPane root = new StackPane();
    private final SubScene subscene;

    public TestPlot() {
      subscene = new SubScene(root, 1, 1, true, SceneAntialiasing.BALANCED);
      subscene.setCamera(camera);
      getChildren().add(subscene);

      widthProperty().addListener((obs, oldVal, newVal) -> refreshPlot());
      heightProperty().addListener((obs, oldVal, newVal) -> refreshPlot());
      refreshPlot();
    }

    private void refreshPlot() {
      // Set the subscene bounds to match the plot bounds, in case the plot was
      // resized
      subscene.setHeight(this.getHeight());
      subscene.setWidth(this.getWidth());

      // Clear any existing stuff
      root.getChildren().clear();
      root.setStyle("-fx-background-color: rgba(0, 0, 0, 0);");
      root.getChildren().add(camera);
      camera.getTransforms().clear();

      int xDataPoints = data.length;
      int zDataPoints = data[0].length;

      // Create data mesh
      TriangleMesh mesh = new TriangleMesh();
      for (int x = 0; x < xDataPoints; x++) {
        for (int z = 0; z < zDataPoints; z++) {
          // Invert the data as JavaFX meshes are positive-down, whereas we expect
          // the plot to be positive-up
          mesh.getPoints().addAll(x, (float) (-data[x][z]), z);
        }
      }

      // Create faces from data mesh
      for (int x = 0; x < xDataPoints - 1; x++) {
        for (int z = 0; z < zDataPoints - 1; z++) {
          int tl = x * zDataPoints + z; // top-left
          int bl = x * zDataPoints + z + 1; // bottom-left
          int tr = (x + 1) * zDataPoints + z; // top-right
          int br = (x + 1) * zDataPoints + z + 1; // bottom-right

          int offset = (x * (zDataPoints - 1) + z) * 8 / 2; // div 2 because we have u AND v in the list

          // working
          mesh.getFaces().addAll(bl, offset + 1, tl, offset + 0, tr, offset + 2);
          mesh.getFaces().addAll(tr, offset + 2, br, offset + 3, bl, offset + 1);
        }
      }

      // Create data mesh texture map
      for (float x = 0; x < xDataPoints - 1; x++) {
        for (float z = 0; z < zDataPoints - 1; z++) {
          float x0 = x / xDataPoints;
          float z0 = z / zDataPoints;
          float x1 = (x + 1) / xDataPoints;
          float z1 = (z + 1) / zDataPoints;

          mesh.getTexCoords().addAll( //
              x0, z0, // 0, top-left
              x0, z1, // 1, bottom-left
              x1, z1, // 2, top-right
              x1, z1 // 3, bottom-right
          );
        }
      }

      // Create texture material
      Image diffuseMap = createTexture(data);
      PhongMaterial material = new PhongMaterial();
      material.setDiffuseMap(diffuseMap);

      // Create & add mesh view
      MeshView meshView = new MeshView(mesh);
      meshView.setTranslateZ(-zDataPoints);
      meshView.setMaterial(material);
      meshView.setCullFace(CullFace.NONE);
      meshView.setDrawMode(DrawMode.FILL);
      meshView.setDepthTest(DepthTest.ENABLE);
      root.getChildren().addAll(meshView);

      double biggestAxisSize = xDataPoints;
      double z = -(0.5 * biggestAxisSize) / Math.tan(0.5 * Math.toRadians(camera.getFieldOfView()));
      camera.getTransforms().addAll(
          new Translate(0, 0, -zDataPoints / 3.0),
          new Rotate(-30, Rotate.X_AXIS),
          new Translate(0, 0.5, z)
      );
      camera.setFarClip(biggestAxisSize * 200.0);
    }

    private Image createTexture(double[][] data) {
      int width = data.length;
      int height = data[0].length;

      WritableImage wr = new WritableImage(width, height);
      PixelWriter pw = wr.getPixelWriter();
      for (int x = 0; x < width; x++) {
        for (int y = 0; y < height; y++) {
          pw.setColor(x, y, Color.RED);
        }
      }
      return wr;
    }
  }
}
javafx 3d
1个回答
2
投票

修改cullFace propertycullFace

MeshView

此外,您还需要向场景中添加环境光。表面法线是自动确定的,如果法线背对光源,则使用的标量乘积将不是正值。

meshView.setCullFace(CullFace.NONE);
© www.soinside.com 2019 - 2024. All rights reserved.