如何在OpenGL / WebGL片段着色器中渲染纹理的子集(知道像素大小)?

时间:2014-10-11 11:36:42

标签: opengl-es dart webgl shader fragment-shader

我有一些代码将2D精灵渲染到屏幕上。一切正常;但纹理坐标是在0,0 - 1,1范围内提供的。我想以像素为单位提供坐标,这样当我创建一个精灵时,我可以提供精灵表的一部分以像素为单位渲染(否则,如果我的spritesheet会改变大小,我会' d需要重新计算所有位置,这看起来并不正常。)

// 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中的纹理是否允许我访问它(也不是我确定这是正常的事情。)

我试图尽可能多地在着色器中进行计算(我认为这是合乎逻辑的,因为GPU往往比CPU更快,但就像我说的那样,我是noob,请指出如果这很愚蠢!),我当前的着色器看起来像这样:

# 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));
}

目前,使用我的片段着色器的vTextureSize0,0 - 1,1范围内工作。能够为我的纹理提供像素坐标并将它们映射到某个地方的正确方法是什么?

我认为可能有一个用于基本2D渲染的常用/标准着色器设置,它已经有一堆我可以提供的制服/属性,但我一直找不到。然而,如果存在这样的事情,我很想知道(因为我甚至没有进行旋转,alpha,着色以及其他我可能想要做的精灵; - )

1 个答案:

答案 0 :(得分:0)

虽然我在发布时找不到任何样本。接受完整的TextureSize并除以它可能并不罕见; XNA SpriteBatch着色器is here online就是这么做的。这可能是任何其他人缺席的良好起点! :)

(为方便起见,请在下方附上来源,因为它已在所提供的链接中以压缩方式包装。许可证为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();
    }
}