【问题标题】:Issue with a shader and texture coordinates着色器和纹理坐标问题
【发布时间】:2018-06-26 23:34:06
【问题描述】:

我有一个我正在尝试使用的着色器,但我遇到了一个我无法解决的问题,因为我对 glsl 的了解有限。 我使用纹理作为蒙版并调试此问题,我只是将此纹理像素颜色用作 gl_FragColor,我将发布一些图像以显示它的外观和外观。

图片链接; https://imgur.com/EBt2vbL

这似乎与 gl_TexCoord[0].xy 的坐标有关,没有得到溶解纹理的正确坐标

main.cpp

#include "Engine.h"

#include <stdio.h>
#include <iostream>
#include <windows.h>

int main(int argc, char *argv[])
{
    try
    {
        Engine game;
        game.Run();
    }
    catch (std::exception& err)
    {
        std::cout << "\nException: " << err.what() << std::endl;
    }

    return 0;
}

引擎.h

#pragma once

#include <SFML/System.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <SFML/Audio.hpp>
#include <SFML/Network.hpp>

#include <vector>
#include <iostream>

class Engine
{
public:
    Engine();

    void Run();
    void HandleEvents(sf::Time deltaTime);
    void Update(sf::Time deltaTime);
    void BuildVertices();
    void Draw();

private:
    bool running;
    bool hasFocus;
    bool fullScreen;


    sf::RenderWindow mainWindow;
    sf::Time deltaTime;
    sf::Event event;

    sf::Vector2i screenResolution;
    sf::Vector2i mousePosition;

    sf::VertexArray vertices;
    sf::Vertex vertex;
    sf::Shader dissolveShader;
    sf::Texture dissolveTexture;
    sf::RenderStates renderState;
    float dissolveValue;

    sf::Texture objectSpriteSheetTexture;
};

Engine.cpp

#include "Engine.h"

static const sf::Time TimePerFrame = sf::seconds(1.f / 60.f);

Engine::Engine()
    : hasFocus(true)
    , fullScreen(fullScreen)
    , running(false)
    , dissolveValue(1.0f)
    , vertices(sf::Quads)
{

    mainWindow.create(sf::VideoMode(640, 480), "Test", sf::Style::Titlebar);
    mainWindow.setPosition(sf::Vector2i(0, 0));
    screenResolution.x = 640;
    screenResolution.y = 480;

    // 512x512 sheet, each sprite is 128x128
    if (!objectSpriteSheetTexture.loadFromFile("ObjectSheet.png"))
        std::cout << "failed to load ObjectSheet.png" << std::endl;

    if (!dissolveTexture.loadFromFile("DissolveTexture.png"))
        std::cout << "failed to load DissolveTexture.png" << std::endl;

    if (!dissolveShader.loadFromFile("DissolveShader.frag", sf::Shader::Fragment))
    {
        std::cout << "failed to load DissolveShader.frag" << std::endl;
    }

    dissolveShader.setUniform("sourceTexture", sf::Shader::CurrentTexture);
    dissolveShader.setUniform("dissolveTexture", dissolveTexture);

    renderState.shader = &dissolveShader;
    renderState.texture = &objectSpriteSheetTexture;
}



void Engine::Run()
{
    // main loop
    sf::Clock clock;
    sf::Time timeSinceLastUpdate = sf::Time::Zero;

    sf::Time elapsedTime;

    running = true;

    while(running)
    {
        elapsedTime = clock.restart();
        timeSinceLastUpdate += elapsedTime;

        HandleEvents(TimePerFrame);
        while(timeSinceLastUpdate > TimePerFrame)
        {
            timeSinceLastUpdate -= TimePerFrame;
            Update(TimePerFrame);
        }

        BuildVertices();
        Draw();
    }
}


void Engine::HandleEvents(sf::Time deltaTime)
{
    mousePosition = sf::Mouse::getPosition(mainWindow);

    while(mainWindow.pollEvent(event))
    {
        if(event.type == sf::Event::Closed)
            mainWindow.close();

        if (event.type == sf::Event::KeyPressed)
        {
            if (event.key.code == sf::Keyboard::Escape)
            {
                running = false;
            }
        }
    }
}

void Engine::Update(sf::Time deltaTime)
{
}

void Engine::BuildVertices()
{
    vertices.clear();

    int frameSize = 128;

    sf::Vector2i objectPosition(100, 100);
    sf::Vector2i spriteSheetTextureCoordinates(0, 128);

    vertex.position.x = objectPosition.x;
    vertex.position.y = objectPosition.y;
    vertex.texCoords.x = spriteSheetTextureCoordinates.x;
    vertex.texCoords.y = spriteSheetTextureCoordinates.y;
    vertices.append(vertex);

    vertex.position.x = objectPosition.x + frameSize;
    vertex.position.y = objectPosition.y;
    vertex.texCoords.x = spriteSheetTextureCoordinates.x + frameSize;
    vertex.texCoords.y = spriteSheetTextureCoordinates.y;
    vertices.append(vertex);

    vertex.position.x = objectPosition.x + frameSize;
    vertex.position.y = objectPosition.y + frameSize;
    vertex.texCoords.x = spriteSheetTextureCoordinates.x + frameSize;
    vertex.texCoords.y = spriteSheetTextureCoordinates.y + frameSize;
    vertices.append(vertex);

    vertex.position.x = objectPosition.x;
    vertex.position.y = objectPosition.y + frameSize;
    vertex.texCoords.x = spriteSheetTextureCoordinates.x;
    vertex.texCoords.y = spriteSheetTextureCoordinates.y + frameSize;
    vertices.append(vertex);
}

void Engine::Draw()
{
    mainWindow.clear(sf::Color::Black);

    dissolveShader.setUniform("dissolveValue", dissolveValue);

    mainWindow.draw(vertices, renderState);

    mainWindow.display();
}

顶点着色器是由 sfml 处理的标准传递。


片段着色器;

#version 130

// used as the mask to determine if a pixel of the source texture should be drawn, 128x128
uniform sampler2D dissolveTexture;

// the texture of the object i'm drawing, a 128x128 part of a 512x512 sprite sheet
uniform sampler2D sourceTexture;

// set to 1.0 for debug 
uniform float dissolveValue;

void main( void ) 
{
    vec4 sourceColor = texture2D(sourceTexture, gl_TexCoord[0].xy);
    vec4 maskColor = texture2D(dissolveTexture, gl_TexCoord[0].xy);

    if(maskColor.r <= dissolveValue)
    {
        // it would return the source pixel color here one the issue is solved
        // gl_FragColor = sourceColor;

        // debuging, so returning the mask textures pixel color
        gl_FragColor = maskColor;
    }
    else
    {
        gl_FragColor = sourceColor;
    }
}

我可能忽略了一些简单的事情,所以如果有人能指出我正确的方向,我将不胜感激,谢谢!

【问题讨论】:

  • 我们需要一个minimal reproducible example
  • 对,我能做到,很快就搞定,得用完一个小时
  • 添加了最小示例,谢谢
  • 纹理坐标应该在 [0-1] 范围内,尽管它可以超出该范围,但这是为了别的。您应该调试并检查分配的 tex 坐标的值
  • 我知道着色器的这一点,但是 sfml 不使用相同的坐标系。我也不确定如何像调试 c++ 那样调试着色器代码

标签: c++ opengl glsl sfml fragment-shader


【解决方案1】:

纹理坐标,对于 GLSL 函数texture (formerly texture2D),范围从 0.0 到 1.0,其中 (0.0, 0.0) 通常是左下角, (1.0, 1.0) 是纹理图像的右上角。

但是,SFML 库按当前纹理的大小 (sf::Shader::CurrentTexture) 缩放纹理坐标。这意味着纹理坐标必须设置在当前纹理大小的范围内:

这意味着你必须像这样设置纹理坐标:

void Engine::BuildVertices()
{
    vertices.clear();

    int frameSize = 128;
    sf::Vector2i objectPosition(100, 100);

    sf::Vector2i texSize(512, 512);

    vertex.position  = sf::Vector2f(objectPosition.x, objectPosition.y);
    vertex.texCoords = sf::Vector2f(0.0f, 0.0f);
    vertices.append(vertex);

    vertex.position  = sf::Vector2f(objectPosition.x + frameSize, objectPosition.y);
    vertex.texCoords = sf::Vector2f(texSize.x, 0.0f);
    vertices.append(vertex);

    vertex.position  = sf::Vector2f(objectPosition.x + frameSize, objectPosition.y + frameSize);
    vertex.texCoords = sf::Vector2f(texSize.x, texSize.y);
    vertices.append(vertex);

    vertex.position  = sf::Vector2f(objectPosition.x, objectPosition.y + frameSize);
    vertex.texCoords = sf::Vector2f(0.0f, texSize.y);
    vertices.append(vertex);
}


你有一个大小为 128*128 的蒙版纹理,你有一个大小为 512*512 的平铺精灵(4*4 瓦片)。我建议在片段着色器中添加纹理坐标偏移统一(texOffset)和纹理比例统一(texScale),并允许选择纹理的平铺:

#version 130

uniform sampler2D dissolveTexture;
uniform sampler2D sourceTexture;
uniform float     dissolveValue;
uniform vec2      texScale;
uniform vec2      texOffset;

void main( void ) 
{
    vec4 sourceColor = texture2D(sourceTexture, texOffset+texScale*gl_TexCoord[0].xy);
    vec4 maskColor   = texture2D(dissolveTexture, gl_TexCoord[0].xy);

    gl_FragColor = mix( sourceColor, maskColor, step(maskColor.r, dissolveValue) );
}

您必须在函数Draw 中设置制服。比例由瓷砖行数和列数的倒数给出。偏移量是图块的索引乘以比例因子:

void Engine::Draw()
{
    mainWindow.clear(sf::Color::Black);

    dissolveValue = 0.5f;
    dissolveShader.setUniform("dissolveValue", dissolveValue);

    float scale_x = 1.0f/4.0f;
    float scale_y = 1.0f/4.0f;
    int i_x = 1; // column of tile (form 0 to 3)
    int i_y = 2; // row of tile (form 0 to 3)

    dissolveShader.setUniform("texScale", sf::Glsl::Vec2(scale_x, scale_y));
    dissolveShader.setUniform("texOffset", sf::Glsl::Vec2(i_x*scale_x, i_y*scale_y));

    mainWindow.draw(vertices, renderState);

    mainWindow.display();
}

【讨论】:

  • 您好,是的,我知道着色器中的坐标介于 0.0 和 1.0 之间,但是如果我在构建顶点时使用 0.0 到 1.0 的范围,则 sfml 中的顶点纹理坐标不会sfml 它只会显示拉伸到帧大小的 1 个像素。问题似乎是 gl_TexCoord[0] 给出了精灵表中的纹理坐标(在适当的 0.0 到 1.0 中),而我要求它们从 0.0 开始并以 1.0 结束,我不确定如何真正解释它。精灵表是 512x512,这一帧从 0、128 开始,遮罩纹理是 128x128,从 0,0 开始
  • 是的,如果我使用从 0 到 512 的纹理坐标构建顶点,它将正确显示蒙版纹理。我想渲染 spritesheet 的一部分并使用着色器根据一个值来掩盖它的一部分,但是我无法从着色器中获得正确的掩码纹理坐标,我得到的坐标似乎与spritesheet 包括偏移量,如果您检查我上面链接的图像,您可以看到它以 4:1 像素比渲染拉伸区域并具有偏移量,spritesheet 纹理是蒙版纹理大小的 4 倍
  • 没错,除非我尝试获取遮罩纹理的纹理坐标(在着色器中),它们匹配 512x512 纹理(它们从与偏移量等匹配的位置开始)
  • 不,掩码不平铺,所有平铺使用相同的掩码
  • 谢谢Rabbid76,你的解决方案帮我解决了这个问题,我不得不改变一些东西,因为我发布的着色器是我创建的一个更大的着色器的一部分,这部分也是+1 "gl_FragColor = mix(sourceColor , maskColor, step(maskColor.r, solveValue) ); 很好地避免在着色器中使用 if 语句,如果我想跳过高于溶解值的片段,同样的工作吗?避免 if 语句是
猜你喜欢
  • 2012-03-05
  • 2016-01-10
  • 2020-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-20
  • 1970-01-01
相关资源
最近更新 更多