web-dev-qa-db-ja.com

OpenGLESで球を描く

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

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

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

14
HungryCoder

これにOpenGLES 2.0のタグを付けたので、滑らかな球を作成するための代替アプローチを提案します。それは、レイトレーシングされた詐欺師としてそれらを描画することです。滑らかな球を複製するために必要な多くの頂点を計算するのではなく、球がどの角度から見てもほとんど同じに見えるという事実を利用できます。

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

Sphere impostor generation

2つの三角形を表す4つの頂点を頂点シェーダーに送信します。頂点シェーダーはそれらを移動して、常にユーザーに面する正方形を作成します。その正方形内で、フラグメントシェーダーを使用して各ピクセルをラスターし、この正方形のウィンドウを通して球を表示した場合にその時点で球が持つ色を提供します。

このアプローチの利点は、球がディスプレイの解像度と同じくらい滑らかであり、ジオメトリの再計算を必要とせずに球を小さいものから大きいものに簡単にスケーリングできることです。レンダリングの負担を頂点プロセッサからフラグメントプロセッサに移しますが、単一の球体の場合、これまで使用したOpenGL ES2.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);
}

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

45
Brad Larson