Как визуализировать подмножество текстуры (зная размер в пикселях) в фрагментном шейдере OpenGL/WebGL?

У меня есть код, который отображает 2D-спрайты на экране. Все работает; но координаты текстуры указаны в диапазоне 0,0 - 1,1. Я хотел бы указать координаты в пикселях, чтобы при создании спрайта я мог предоставить часть листа спрайтов для рендеринга в пикселях (в противном случае, если мой лист спрайтов когда-либо изменит размер, мне нужно будет заново рассчитать все локации, что не кажется нормальным).

// IDEAL SPRITE INIT CODE
var player = new Sprite(
  position: new Vector2.zero(),
  velocity: new Vector2.zero(),
  size: new Vector2(128.0, 128.0) // Rendered size in world-units
  texture: player2Texture,
  textureOffset: new Vector2.zero(), // Offset in spritesheet
  textureSize: new Vector2(100, 100), // Size of section of spritesheet to render
);

Я мог бы передать здесь общий размер текстуры и разделить на него, чтобы получить числа в диапазоне 0-1, но я не вижу, дает ли мне Текстура в WebGL доступ к этому (и я не уверен, что это обычное дело).

Я пытаюсь сделать как можно больше вычислений в шейдере (полагаю, это логично, поскольку графические процессоры, как правило, быстрее, чем процессоры, но, как я уже сказал, я нуб, пожалуйста, укажите, если это глупо!) , и мои текущие шейдеры выглядят так:

# VERTEXT SHADER
uniform vec2 uResolution;

attribute vec2 aSpriteLocation;
attribute vec2 aSpriteSize;
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;
attribute vec2 aTextureSize;

varying vec2 vTextureCoord;
varying vec2 vTextureSize;

void main() {
  // Convert from screen coords to clipSpace (-1,-1 to 1,1)
  vec2 clipSpace = (((aSpriteLocation + (aVertexPosition * aSpriteSize)) / uResolution) * 2.0) - 1.0;

  // Flip upside down, so 0,0 is at the top of the screen!
  clipSpace = clipSpace * vec2(1, -1);

  gl_Position = vec4(clipSpace, 0.0, 1.0);
  vTextureCoord = aTextureCoord;
  vTextureSize = aTextureSize;
}
# FRAGMENT SHADER
#ifdef GL_ES
precision highp float;
#endif

uniform sampler2D uSampler;

varying vec2 vTextureCoord;
varying vec2 vTextureSize; # Currently this must be in the range 0,0 - 1,1; but I want to pass in texture pixels

void main() {
  gl_FragColor = texture2D(uSampler, vTextureSize * vec2(vTextureCoord.s, vTextureCoord.t));
}

В настоящее время vTextureSize, который попадает в мой фрагментный шейдер, работает в диапазоне 0,0 - 1,1. Каков правильный способ предоставить пиксельные координаты для моей текстуры и где-то их сопоставить?

Я думал, что может быть общий/стандартный набор шейдеров, используемый для базового 2D-рендеринга, в котором уже есть набор юниформ/атрибутов, которые я мог бы просто предоставить, но я не смог его найти. Однако, если такие вещи существуют, я хотел бы знать (поскольку я даже не добрался до вращения, альфы, тонирования и других вещей, которые я мог бы захотеть сделать со спрайтами ;-))


person Danny Tuppeny    schedule 11.10.2014    source источник


Ответы (1)


Хотя я не мог найти никаких образцов, когда я публиковал; получение полного TextureSize и деление на него может быть обычным делом; шейдер XNA SpriteBatch здесь онлайн и делает именно это . Это может быть хорошей отправной точкой в ​​отсутствие каких-либо других! :)

(Включая исходный код ниже для удобства, так как он упакован в zip по предоставленной ссылке. Лицензия MS-PL)

//-----------------------------------------------------------------------------
// SpriteBatch.fx
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------


// Input parameters.
float2   ViewportSize    : register(c0);
float2   TextureSize     : register(c1);
float4x4 MatrixTransform : register(c2);
sampler  TextureSampler  : register(s0);


#ifdef XBOX360


// Vertex shader for rendering sprites on Xbox.
void SpriteVertexShader(int        index          : INDEX,
                        out float4 outputPosition : POSITION0,
                        out float4 outputColor    : COLOR0,
                        out float2 outputTexCoord : TEXCOORD0)
{
    // Read input data from the vertex buffer.
    float4 source;
    float4 destination;
    float4 originRotationDepth;
    float4 effects;
    float4 color;

    int vertexIndex = index / 4;
    int cornerIndex = index % 4;

    asm
    {
        vfetch source,              vertexIndex, texcoord0
        vfetch destination,         vertexIndex, texcoord1
        vfetch originRotationDepth, vertexIndex, texcoord2
        vfetch effects,             vertexIndex, texcoord3
        vfetch color,               vertexIndex, texcoord4
    };

    // Unpack into local variables, to make the following code more readable.
    float2 texCoordPosition = source.xy;
    float2 texCoordSize = source.zw;

    float2 position = destination.xy;
    float2 size = destination.zw;

    float2 origin = originRotationDepth.xy;
    float rotation = originRotationDepth.z;
    float depth = originRotationDepth.w;

    // Which of the four sprite corners are we currently shading?
    float2 whichCorner;

    if      (cornerIndex == 0) whichCorner = float2(0, 0);
    else if (cornerIndex == 1) whichCorner = float2(1, 0);
    else if (cornerIndex == 2) whichCorner = float2(1, 1);
    else                       whichCorner = float2(0, 1);

    // Calculate the vertex position.
    float2 cornerOffset = (whichCorner - origin / texCoordSize) * size;

    // Rotation.
    float cosRotation = cos(rotation);
    float sinRotation = sin(rotation);

    position += mul(cornerOffset, float2x2(cosRotation, sinRotation, -sinRotation, cosRotation));

    // Apply the matrix transform.
    outputPosition = mul(float4(position, depth, 1), transpose(MatrixTransform));

    // Half pixel offset for correct texel centering.
    outputPosition.xy -= 0.5;

    // Viewport adjustment.
    outputPosition.xy /= ViewportSize;
    outputPosition.xy *= float2(2, -2);
    outputPosition.xy -= float2(1, -1);

    // Texture mirroring.
    whichCorner = lerp(whichCorner, 1 - whichCorner, effects);

    // Compute the texture coordinate.
    outputTexCoord = (texCoordPosition + whichCorner * texCoordSize) / TextureSize;

    // Simple color output.
    outputColor = color;
}


#else


// Vertex shader for rendering sprites on Windows.
void SpriteVertexShader(inout float4 position : POSITION0,
                        inout float4 color    : COLOR0,
                        inout float2 texCoord : TEXCOORD0)
{
    // Apply the matrix transform.
    position = mul(position, transpose(MatrixTransform));

    // Half pixel offset for correct texel centering.
    position.xy -= 0.5;

    // Viewport adjustment.
    position.xy /= ViewportSize;
    position.xy *= float2(2, -2);
    position.xy -= float2(1, -1);

    // Compute the texture coordinate.
    texCoord /= TextureSize;
}


#endif


// Pixel shader for rendering sprites (shared between Windows and Xbox).
void SpritePixelShader(inout float4 color : COLOR0, float2 texCoord : TEXCOORD0)
{
    color *= tex2D(TextureSampler, texCoord);
}


technique SpriteBatch
{
    pass
    {
        VertexShader = compile vs_1_1 SpriteVertexShader();
        PixelShader  = compile ps_1_1 SpritePixelShader();
    }
}
person Danny Tuppeny    schedule 11.10.2014