万维网自定义渲染选择问题

11

我正在学习这个教程

每当我的鼠标悬停在使用这段代码创建的立方体上时(下面是我的版本),大气层和星星就会消失。

这是正常情况下的外观:

enter image description here

这是当我将鼠标悬停在立方体上时的外观(请注意大气层):

enter image description here

我不确定这里发生了什么。

/*
 * Copyright (C) 2012 United States Government as represented by the Administrator of the
 * National Aeronautics and Space Administration.
 * All Rights Reserved.
 */

package gov.nasa.worldwindx.examples.tutorial;

import gov.nasa.worldwind.Configuration;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.layers.RenderableLayer;
import gov.nasa.worldwind.pick.PickSupport;
import gov.nasa.worldwind.render.*;
import gov.nasa.worldwind.util.OGLUtil;
import gov.nasa.worldwindx.examples.ApplicationTemplate;

import javax.media.opengl.*;
import java.awt.*;

/**
 * Example of a custom {@link Renderable} that draws a cube at a geographic position. This class shows the simplest
 * possible example of a custom Renderable, while still following World Wind best practices. See
 * http://goworldwind.org/developers-guide/how-to-build-a-custom-renderable/ for a complete description of this
 * example.
 *
 * @author pabercrombie
 * @version $Id: Cube.java 691 2012-07-12 19:17:17Z pabercrombie $
 */
public class Cube extends ApplicationTemplate implements Renderable
{
    /** Geographic position of the cube. */
    protected Position position;
    /** Length of each face, in meters. */
    protected double size;

    /** Support object to help with pick resolution. */
    protected PickSupport pickSupport = new PickSupport();

    // Determined each frame
    protected long frameTimestamp = -1L;
    protected OrderedCube currentFramesOrderedCube;

    /**
     * This class holds the Cube's Cartesian coordinates. An instance of it is added to the scene controller's ordered
     * renderable queue during picking and rendering.
     */
    protected class OrderedCube implements OrderedRenderable
    {
        /** Cartesian position of the cube, computed from
         * {@link gov.nasa.worldwindx.examples.tutorial.Cube#position}. */
        protected Vec4 placePoint;
        /** Distance from the eye point to the cube. */
        protected double eyeDistance;
        /**
         * The cube's Cartesian bounding extent.
         */
        protected Extent extent;

        public double getDistanceFromEye()
        {
            return this.eyeDistance;
        }

        public void pick(DrawContext dc, Point pickPoint)
        {
            // Use same code for rendering and picking.
            this.render(dc);
        }

        public void render(DrawContext dc)
        {
            Cube.this.drawOrderedRenderable(dc, Cube.this.pickSupport);
        }
    }

    public Cube(Position position, double sizeInMeters)
    {
        this.position = position;
        this.size = sizeInMeters;
    }

    public void render(DrawContext dc)
    {
        // Render is called twice, once for picking and once for rendering. In both cases an OrderedCube is added to
        // the ordered renderable queue.

        OrderedCube orderedCube = this.makeOrderedRenderable(dc);

        if (orderedCube.extent != null)
        {
            if (!this.intersectsFrustum(dc, orderedCube))
                return;

            // If the shape is less that a pixel in size, don't render it.
            if (dc.isSmall(orderedCube.extent, 1))
                return;
        }

        // Add the cube to the ordered renderable queue. The SceneController sorts the ordered renderables by eye
        // distance, and then renders them back to front.
        dc.addOrderedRenderable(orderedCube);
    }

    /**
     * Determines whether the cube intersects the view frustum.
     *
     * @param dc the current draw context.
     *
     * @return true if this cube intersects the frustum, otherwise false.
     */
    protected boolean intersectsFrustum(DrawContext dc, OrderedCube orderedCube)
    {
        if (dc.isPickingMode())
            return dc.getPickFrustums().intersectsAny(orderedCube.extent);

        return dc.getView().getFrustumInModelCoordinates().intersects(orderedCube.extent);
    }

    /**
     * Compute per-frame attributes, and add the ordered renderable to the ordered renderable list.
     *
     * @param dc Current draw context.
     */
    protected OrderedCube makeOrderedRenderable(DrawContext dc)
    {
        // This method is called twice each frame: once during picking and once during rendering. We only need to
        // compute the placePoint, eye distance and extent once per frame, so check the frame timestamp to see if
        // this is a new frame. However, we can't use this optimization for 2D continuous globes because the
        // Cartesian coordinates of the cube are different for each 2D globe drawn during the current frame.

        if (dc.getFrameTimeStamp() != this.frameTimestamp || dc.isContinuous2DGlobe())
        {
            OrderedCube orderedCube = new OrderedCube();

            // Convert the cube's geographic position to a position in Cartesian coordinates. If drawing to a 2D
            // globe ignore the shape's altitude.
            if (dc.is2DGlobe())
            {
                orderedCube.placePoint = dc.getGlobe().computePointFromPosition(this.position.getLatitude(),
                    this.position.getLongitude(), 0);
            }
            else
            {
                orderedCube.placePoint = dc.getGlobe().computePointFromPosition(this.position);
            }

            // Compute the distance from the eye to the cube's position.
            orderedCube.eyeDistance = dc.getView().getEyePoint().distanceTo3(orderedCube.placePoint);

            // Compute a sphere that encloses the cube. We'll use this sphere for intersection calculations to determine
            // if the cube is actually visible.
            orderedCube.extent = new Sphere(orderedCube.placePoint, Math.sqrt(3.0) * this.size / 2.0);

            // Keep track of the timestamp we used to compute the ordered renderable.
            this.frameTimestamp = dc.getFrameTimeStamp();
            this.currentFramesOrderedCube = orderedCube;

            return orderedCube;
        }
        else
        {
            return this.currentFramesOrderedCube;
        }
    }

    /**
     * Set up drawing state, and draw the cube. This method is called when the cube is rendered in ordered rendering
     * mode.
     *
     * @param dc Current draw context.
     */
    protected void drawOrderedRenderable(DrawContext dc, PickSupport pickCandidates)
    {
        this.beginDrawing(dc);
        try
        {
            GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
            if (dc.isPickingMode())
            {
                Color pickColor = dc.getUniquePickColor();
                pickCandidates.addPickableObject(pickColor.getRGB(), this, this.position);
                gl.glColor3ub((byte) pickColor.getRed(), (byte) pickColor.getGreen(), (byte) pickColor.getBlue());
            }

            // Render a unit cube and apply a scaling factor to scale the cube to the appropriate size.
            gl.glScaled(this.size, this.size, this.size);
            this.drawUnitCube(dc);
        }
        finally
        {
            this.endDrawing(dc);
        }
    }

    /**
     * Setup drawing state in preparation for drawing the cube. State changed by this method must be restored in
     * endDrawing.
     *
     * @param dc Active draw context.
     */
    protected void beginDrawing(DrawContext dc)
    {
        GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.

        int attrMask = GL2.GL_CURRENT_BIT | GL2.GL_COLOR_BUFFER_BIT;

        gl.glPushAttrib(attrMask);

        if (!dc.isPickingMode())
        {
            dc.beginStandardLighting();
            gl.glEnable(GL.GL_BLEND);
            OGLUtil.applyBlending(gl, false);

            // Were applying a scale transform on the modelview matrix, so the normal vectors must be re-normalized
            // before lighting is computed.
            gl.glEnable(GL2.GL_NORMALIZE);
        }

        // Multiply the modelview matrix by a surface orientation matrix to set up a local coordinate system with the
        // origin at the cube's center position, the Y axis pointing North, the X axis pointing East, and the Z axis
        // normal to the globe.
        gl.glMatrixMode(GL2.GL_MODELVIEW);

        Matrix matrix = dc.getGlobe().computeSurfaceOrientationAtPosition(this.position);
        matrix = dc.getView().getModelviewMatrix().multiply(matrix);

        double[] matrixArray = new double[16];
        matrix.toArray(matrixArray, 0, false);
        gl.glLoadMatrixd(matrixArray, 0);
    }

    /**
     * Restore drawing state changed in beginDrawing to the default.
     *
     * @param dc Active draw context.
     */
    protected void endDrawing(DrawContext dc)
    {
        GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.

        if (!dc.isPickingMode())
            dc.endStandardLighting();

        gl.glPopAttrib();
    }

    /**
     * Draw a unit cube, using the active modelview matrix to orient the shape.
     *
     * @param dc Current draw context.
     */
    protected void drawUnitCube(DrawContext dc)
    {
        // Vertices of a unit cube, centered on the origin.
        float[][] v = {{-0.5f, 0.5f, -0.5f}, {-0.5f, 0.5f, 0.5f}, {0.5f, 0.5f, 0.5f}, {0.5f, 0.5f, -0.5f},
            {-0.5f, -0.5f, 0.5f}, {0.5f, -0.5f, 0.5f}, {0.5f, -0.5f, -0.5f}, {-0.5f, -0.5f, -0.5f}};

        // Array to group vertices into faces
        int[][] faces = {{0, 1, 2, 3}, {2, 5, 6, 3}, {1, 4, 5, 2}, {0, 7, 4, 1}, {0, 7, 6, 3}, {4, 7, 6, 5}};

        // Normal vectors for each face
        float[][] n = {{0, 1, 0}, {1, 0, 0}, {0, 0, 1}, {-1, 0, 0}, {0, 0, -1}, {0, -1, 0}};

        // Note: draw the cube in OpenGL immediate mode for simplicity. Real applications should use vertex arrays
        // or vertex buffer objects to achieve better performance.
        GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
        gl.glBegin(GL2.GL_QUADS);
        try
        {
            for (int i = 0; i < faces.length; i++)
            {
                gl.glNormal3f(n[i][0], n[i][1], n[i][2]);

                for (int j = 0; j < faces[0].length; j++)
                {
                    gl.glVertex3f(v[faces[i][j]][0], v[faces[i][j]][1], v[faces[i][j]][2]);
                }
            }
        }
        finally
        {
            gl.glEnd();
        }
    }

    protected static class AppFrame extends ApplicationTemplate.AppFrame
    {
        public AppFrame()
        {
            super(true, true, false);

            RenderableLayer layer = new RenderableLayer();
            Cube cube = new Cube(Position.fromDegrees(35.0, -120.0, 3000), 100000);
            layer.addRenderable(cube);

            getWwd().getModel().getLayers().add(layer);
        }
    }

    public static void main(String[] args)
    {
        Configuration.setValue(AVKey.INITIAL_LATITUDE, 35.0);
        Configuration.setValue(AVKey.INITIAL_LONGITUDE, -120.0);
        Configuration.setValue(AVKey.INITIAL_ALTITUDE, 2550000);
        Configuration.setValue(AVKey.INITIAL_PITCH, 45);
        Configuration.setValue(AVKey.INITIAL_HEADING, 45);

        ApplicationTemplate.start("World Wind Custom Renderable Tutorial", AppFrame.class);
    }
}

2
还没有完全找到答案,但鼠标悬停和未悬停的区别在于DrawContext处于拾取模式(即dc.isPickingMode为true)。我猜这会影响SkyGradientLayer的渲染。也许这可以帮助您进行调试,同时 - 我可以重现此问题。我会在有时间时尝试解决它。 - J Richard Snape
2
有趣的进一步评论 - 这也发生在worldwindx.jar捆绑的Cube类中。 由于他们的立方体比你的小得多,所以有点难以复制,但是如果您设置正确的视角(倾斜?),则可以做到。 我仍在研究为什么会发生这种情况。 - J Richard Snape
1
感谢您深入研究这个问题 - 我使用的Cube类实际上与worldwindx.jar中的相同,只是将立方体放大以强调问题。我对这里发生的事情感到困惑。 - mainstringargs
1
你打算使用拾取功能吗?因为如果你不想使用它,它可以被禁用。 - Nikolay Tomitov
1
是的,我希望能够解决这个问题,这也是我首先注意到这个问题的原因。我正在开发另一个应用程序,它使用WorldWind和自定义渲染器,也遇到了这个问题 - 这就是我注意到他们的Cube示例中也存在这个问题的原因。 - mainstringargs
显示剩余2条评论
3个回答

1
我已经重现了问题,无论是在包含在worldwind.jar中的教程Cube类还是在您绘制一个更大的方块的Cube类中。
我追踪到大气消失的点是当WorldWind渲染代码中交换GLCanvas的隐藏缓冲区时,因此问题在于由于某种原因,在使用您的代码进行拾取模式渲染时,大气层和星层未在隐藏缓冲区上绘制。
然后,我发现包含Cylinders作为gov.nasa.worldwindx.examples.Cylinders.class的示例在地球仪上绘制可选的3D形状(圆柱体),并且不会出现此问题(还有其他示例-例如Boxes非常接近该教程)。
我认为问题出在这个教程示例中OrderedRenderable的实现上 - 在Cylinders示例中,实际的Cylinder类扩展RigidShape,而RigidShape又扩展AbstractShape,这是实际实现OrderedRenderable的类。 这绝对是一个错误。 也许您可以从Boxes示例中获取所需的功能。

感谢您花费时间研究这个问题。我会查看圆柱体和盒子。 - mainstringargs
没问题。真遗憾没有人给你一个恰当的解释。如果我弄清楚了,我会进行编辑。 - J Richard Snape
我在WorldWind论坛上提问并得到了可行的答案,现在在这里发布。再次感谢! - mainstringargs
@dubdubdubdot 太棒了!省去了我继续工作的时间 :) 很高兴你解决了问题,知道问题所在真的很好(一直困扰着我)。 - J Richard Snape

1
我在WorldWind论坛上得到了答案:http://forum.worldwindcentral.com/showthread.php?46115-Worldwind-Custom-Renderable-Picking-Issue&p=125173 在begin drawing方法中添加一个gl push matrix调用:
protected void beginDrawing(DrawContext dc)
    {
        GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.

        int attrMask = GL2.GL_CURRENT_BIT | GL2.GL_COLOR_BUFFER_BIT;

        gl.glPushAttrib(attrMask);

        if (!dc.isPickingMode())
        {
            dc.beginStandardLighting();
            gl.glEnable(GL.GL_BLEND);
            OGLUtil.applyBlending(gl, false);

            // Were applying a scale transform on the modelview matrix, so the normal vectors must be re-normalized
            // before lighting is computed.
            gl.glEnable(GL2.GL_NORMALIZE);
        }

        // Multiply the modelview matrix by a surface orientation matrix to set up a local coordinate system with the
        // origin at the cube's center position, the Y axis pointing North, the X axis pointing East, and the Z axis
        // normal to the globe.
        gl.glPushMatrix();
        gl.glMatrixMode(GL2.GL_MODELVIEW);

        Matrix matrix = dc.getGlobe().computeSurfaceOrientationAtPosition(this.position);
        matrix = dc.getView().getModelviewMatrix().multiply(matrix);

        double[] matrixArray = new double[16];
        matrix.toArray(matrixArray, 0, false);
        gl.glLoadMatrixd(matrixArray, 0);
    }

此外,在绘制结束时添加一个gl pop matrix调用:
protected void endDrawing(DrawContext dc)
    {
        GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.

        if (!dc.isPickingMode())
            dc.endStandardLighting();
        gl.glPopMatrix();
        gl.glPopAttrib();
    }

0

移动

if (!dc.isPickingMode())
   dc.endStandardLighting();

来自

protected void endDrawing(DrawContext dc)

之后

gl.glLoadMatrixd(matrixArray, 0);

protected void beginDrawing(DrawContext dc)

谢谢查看 - 也许我做错了什么,但我仍然遇到问题。这段代码看起来正确吗:http://pastebin.com/Becvr4Gz? - mainstringargs
我不明白为什么这个会生效。这段代码只在不选取的情况下生效,但问题出现在操作人员正在选择时。我尝试放宽 if (!dc.isPickingMode()) 的限制条件,但是没有找到任何改变症状的东西。你有测试过并让气氛保持吗? - J Richard Snape

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接