15

球体を描画したいのですが、glBegin() や glEnd() などの呼び出しを使用して OpenGL でそれを行う方法を知っています。

しかし、ESには何もありません。

提案/チュートリアルのリンク?

4

1 に答える 1

47

これに OpenGL ES 2.0 のタグを付けたので、滑らかな球体を作成する別の方法を提案させてください。滑らかな球を複製するために必要な多くの頂点を計算するのではなく、球がどの角度から見てもほぼ同じに見えるという事実を利用できます。

これを行うには、次のようなプロセスを採用します。

スフィア インポスター生成

2 つの三角形を表す 4 つの頂点を頂点シェーダーに送信すると、それらが置き換えられて、常にユーザーに面する正方形が作成されます。その正方形内で、フラグメント シェーダーを使用して各ピクセルをラスターし、この正方形の窓を通して球体を見た場合に球体がその時点で持つであろう色を提供します。

このアプローチの利点は、球がディスプレイの解像度がサポートするのと同じくらい滑らかであり、ジオメトリを再計算する必要なく、球が小さいものから大きいものに簡単にスケーリングされることです。レンダリングの負荷を頂点プロセッサからフラグメント プロセッサに移すことはできますが、私が使用した OpenGL ES 2.0 デバイスでは、単一の球体についてはそれほど問題になりません。

私はこの iOS アプリケーションでこの手法を使用しており、ソース コードはそのページで入手できます。ここでもう少し詳しく説明します。私が使用する頂点シェーダーの簡略化されたバージョンは、次のようになります。

attribute vec4 position;
attribute vec4 inputImpostorSpaceCoordinate;

varying mediump vec2 impostorSpaceCoordinate;
varying mediump vec3 normalizedViewCoordinate;

uniform mat4 modelViewProjMatrix;
uniform mediump mat4 orthographicMatrix;
uniform mediump float sphereRadius;

void main()
{
    vec4 transformedPosition;
    transformedPosition = modelViewProjMatrix * position;
    impostorSpaceCoordinate = inputImpostorSpaceCoordinate.xy;

    transformedPosition.xy = transformedPosition.xy + inputImpostorSpaceCoordinate.xy * vec2(sphereRadius);
    transformedPosition = transformedPosition * orthographicMatrix;

    normalizedViewCoordinate = (transformedPosition.xyz + 1.0) / 2.0;
    gl_Position = transformedPosition;
}

簡略化されたフラグメント シェーダーは次のとおりです。

precision mediump float;

uniform vec3 lightPosition;
uniform vec3 sphereColor;
uniform mediump float sphereRadius;

uniform sampler2D depthTexture;

varying mediump vec2 impostorSpaceCoordinate;
varying mediump vec3 normalizedViewCoordinate;

const mediump vec3 oneVector = vec3(1.0, 1.0, 1.0);

void main()
{
    float distanceFromCenter = length(impostorSpaceCoordinate);

    // Establish the visual bounds of the sphere
    if (distanceFromCenter > 1.0)
    {
        discard;
    }

    float normalizedDepth = sqrt(1.0 - distanceFromCenter * distanceFromCenter);

    // Current depth
    float depthOfFragment = sphereRadius * 0.5 * normalizedDepth;
    //        float currentDepthValue = normalizedViewCoordinate.z - depthOfFragment - 0.0025;
    float currentDepthValue = (normalizedViewCoordinate.z - depthOfFragment - 0.0025);

    // Calculate the lighting normal for the sphere
    vec3 normal = vec3(impostorSpaceCoordinate, normalizedDepth);

    vec3 finalSphereColor = sphereColor;

    // ambient
    float lightingIntensity = 0.3 + 0.7 * clamp(dot(lightPosition, normal), 0.0, 1.0);
    finalSphereColor *= lightingIntensity;

    // Per fragment specular lighting
    lightingIntensity  = clamp(dot(lightPosition, normal), 0.0, 1.0);
    lightingIntensity  = pow(lightingIntensity, 60.0);
    finalSphereColor += vec3(0.4, 0.4, 0.4) * lightingIntensity;

    gl_FragColor = vec4(finalSphereColor, 1.0);
}

これらのシェーダーの現在の最適化されたバージョンは、従うのが少し難しく、これらには存在しないアンビエント オクルージョン ライティングも使用しています。また、この球のテクスチャリングも示されていません。これは、適切なマッピング関数を使用して、球面座標と長方形のテクスチャの間で変換できます。これは、球体の表面に事前計算されたアンビエント オクルージョン値を提供する方法です。

于 2012-05-08T20:41:54.543 に答える