【问题标题】:Convert floating-point numbers to decimal digits in GLSL在 GLSL 中将浮点数转换为十进制数字
【发布时间】:2017-12-01 07:26:51
【问题描述】:

As others have discussed,GLSL 缺少任何类型的 printf 调试。

但有时我真的想在调试着色器时检查数值。

我一直在尝试创建一个可视化调试工具。 我发现,如果您使用sampler2D,其中数字0123456789 已在等宽空间中呈现,则可以在着色器中相当轻松地呈现任意数字系列。基本上,您只需调整 x 坐标即可。

现在,要使用它来检查浮点数,我需要一种将float 转换为十进制数字序列的算法,就像您可能在任何printf 实现中找到的那样。

不幸的是,as far as I understand the topic,这些算法似乎需要代表 更高精度格式的浮点数,我不知道这是怎么回事 可能在 GLSL 中我似乎只有 32 位 floats 可用。

出于这个原因,我认为这个问题不是任何一般的“printf 如何工作”问题的重复,而是具体关于如何使这些算法在 GLSL 的约束下工作。我见过this question and answer,但不知道那里发生了什么。

我试过的算法不是很好。

我的第一次尝试,标记为版本 A(已注释掉)似乎很糟糕: 举三个随机例子,RenderDecimal(1.0) 呈现为1.099999702RenderDecimal(2.5) 给了我 2.599999246RenderDecimal(2.6)2.699999280 出现。

我的第二次尝试,标记为版本 B,似乎 稍微好一点:1.02.6 都很好,但 RenderDecimal(2.5) 仍然明显不匹配 对5 进行四舍五入,残差为0.099...。结果显示为2.599000022

下面我的最小/完整/可验证示例从一些简短的 GLSL 1.20 代码开始,然后 我碰巧选择了 Python 2.x,只是为了编译着色器并加载和渲染纹理。它需要 pygame、NumPy、PyOpenGL 和 PIL 第三方包。请注意,Python 实际上只是样板文件,可以用 C 或其他任何东西简单地(尽管很乏味)重新编写。只有顶部的 GLSL 代码对这个问题很重要,因此我认为 pythonpython 2.x 标签不会有帮助。

需要将以下图片保存为digits.png

vertexShaderSource = """\

varying vec2 vFragCoordinate;
void main(void)
{
    vFragCoordinate = gl_Vertex.xy;
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

"""
fragmentShaderSource = """\

varying vec2      vFragCoordinate;

uniform vec2      uTextureSize;
uniform sampler2D uTextureSlotNumber;

float OrderOfMagnitude( float x )
{
    return x == 0.0 ? 0.0 : floor( log( abs( x ) ) / log( 10.0 ) );
}
void RenderDecimal( float value )
{
    // Assume that the texture to which uTextureSlotNumber refers contains
    // a rendering of the digits '0123456789' packed together, such that
    const vec2 startOfDigitsInTexture = vec2( 0, 0 ); // the lower-left corner of the first digit starts here and
    const vec2 sizeOfDigit = vec2( 100, 125 ); // each digit spans this many pixels
    const float nSpaces = 10.0; // assume we have this many digits' worth of space to render in
    
    value = abs( value );
    vec2 pos = vFragCoordinate - startOfDigitsInTexture;
    float dpstart = max( 0.0, OrderOfMagnitude( value ) );
    float decimal_position = dpstart - floor( pos.x / sizeOfDigit.x );
    float remainder = mod( pos.x, sizeOfDigit.x );
    
    if( pos.x >= 0 && pos.x < sizeOfDigit.x * nSpaces && pos.y >= 0 && pos.y < sizeOfDigit.y  )
    {
        float digit_value;
        
        // Version B
        float dp, running_value = value;
        for( dp = dpstart; dp >= decimal_position; dp -= 1.0 )
        {
            float base = pow( 10.0, dp );
            digit_value = mod( floor( running_value / base ), 10.0 );
            running_value -= digit_value * base;
        }
        
        // Version A
        //digit_value = mod( floor( value * pow( 10.0, -decimal_position ) ), 10.0 );
        
        

        vec2 textureSourcePosition = vec2( startOfDigitsInTexture.x + remainder + digit_value * sizeOfDigit.x, startOfDigitsInTexture.y + pos.y );
        gl_FragColor = texture2D( uTextureSlotNumber, textureSourcePosition / uTextureSize );
    }
    
    // Render the decimal point
    if( ( decimal_position == -1.0 && remainder / sizeOfDigit.x < 0.1 && abs( pos.y ) / sizeOfDigit.y < 0.1 ) ||
        ( decimal_position ==  0.0 && remainder / sizeOfDigit.x > 0.9 && abs( pos.y ) / sizeOfDigit.y < 0.1 ) )
    {
        gl_FragColor = texture2D( uTextureSlotNumber, ( startOfDigitsInTexture + sizeOfDigit * vec2( 1.5, 0.5 ) ) / uTextureSize );
    }
}

void main(void)
{
    gl_FragColor = texture2D( uTextureSlotNumber, vFragCoordinate / uTextureSize );
    RenderDecimal( 2.5 ); // for current demonstration purposes, just a constant
}

"""

# Python (PyOpenGL) code to demonstrate the above
# (Note: the same OpenGL calls could be made from any language)

import os, sys, time

import OpenGL
from OpenGL.GL import *
from OpenGL.GLU import *

import pygame, pygame.locals # just for getting a canvas to draw on

try: from PIL import Image  # PIL.Image module for loading image from disk
except ImportError: import Image  # old PIL didn't package its submodules on the path

import numpy # for manipulating pixel values on the Python side

def CompileShader( type, source ):
    shader = glCreateShader( type )
    glShaderSource( shader, source )
    glCompileShader( shader )
    result = glGetShaderiv( shader, GL_COMPILE_STATUS )
    if result != 1:
        raise Exception( "Shader compilation failed:\n" + glGetShaderInfoLog( shader ) )
    return shader

class World:
    def __init__( self, width, height ):

        self.window = pygame.display.set_mode( ( width, height ), pygame.OPENGL | pygame.DOUBLEBUF )

        # compile shaders
        vertexShader = CompileShader( GL_VERTEX_SHADER, vertexShaderSource )
        fragmentShader = CompileShader( GL_FRAGMENT_SHADER, fragmentShaderSource )
        # build shader program
        self.program = glCreateProgram()
        glAttachShader( self.program, vertexShader )
        glAttachShader( self.program, fragmentShader )
        glLinkProgram( self.program )
        # try to activate/enable shader program, handling errors wisely
        try:
            glUseProgram( self.program )
        except OpenGL.error.GLError:
            print( glGetProgramInfoLog( self.program ) )
            raise

        # enable alpha blending
        glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE )
        glEnable( GL_DEPTH_TEST )
        glEnable( GL_BLEND )
        glBlendEquation( GL_FUNC_ADD )
        glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA )

        # set projection and background color
        gluOrtho2D( 0, width, 0, height )
        glClearColor( 0.0, 0.0, 0.0, 1.0 )
        
        self.uTextureSlotNumber_addr = glGetUniformLocation( self.program, 'uTextureSlotNumber' )
        self.uTextureSize_addr = glGetUniformLocation( self.program, 'uTextureSize' )

    def RenderFrame( self, *textures ):
        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT )
        for t in textures: t.Draw( world=self )
        pygame.display.flip()
        
    def Close( self ):
        pygame.display.quit()
        
    def Capture( self ):
        w, h = self.window.get_size()
        rawRGB = glReadPixels( 0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE )
        return Image.frombuffer( 'RGB', ( w, h ), rawRGB, 'raw', 'RGB', 0, 1 ).transpose( Image.FLIP_TOP_BOTTOM )
    
class Texture:
    def __init__( self, source, slot=0, position=(0,0,0) ):
        
        # wrangle array
        source = numpy.array( source )
        if source.dtype.type not in [ numpy.float32, numpy.float64 ]: source = source.astype( float ) / 255.0
        while source.ndim < 3: source = numpy.expand_dims( source, -1 )
        if source.shape[ 2 ] == 1: source = source[ :, :, [ 0, 0, 0 ] ]    # LUMINANCE -> RGB
        if source.shape[ 2 ] == 2: source = source[ :, :, [ 0, 0, 0, 1 ] ] # LUMINANCE_ALPHA -> RGBA
        if source.shape[ 2 ] == 3: source = source[ :, :, [ 0, 1, 2, 2 ] ]; source[ :, :, 3 ] = 1.0  # RGB -> RGBA
        # now it can be transferred as GL_RGBA and GL_FLOAT
        
        # housekeeping
        self.textureSize = [ source.shape[ 1 ], source.shape[ 0 ] ]
        self.textureSlotNumber = slot
        self.textureSlotCode = getattr( OpenGL.GL, 'GL_TEXTURE%d' % slot )
        self.listNumber = slot + 1
        self.position = list( position )
        
        # transfer texture content
        glActiveTexture( self.textureSlotCode )
        self.textureID = glGenTextures( 1 )
        glBindTexture( GL_TEXTURE_2D, self.textureID )
        glEnable( GL_TEXTURE_2D )
        glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA32F, self.textureSize[ 0 ], self.textureSize[ 1 ], 0, GL_RGBA, GL_FLOAT, source[ ::-1 ] )
        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST )
        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST )

        # define surface
        w, h = self.textureSize
        glNewList( self.listNumber, GL_COMPILE )
        glBegin( GL_QUADS )
        glColor3f( 1, 1, 1 )
        glNormal3f( 0, 0, 1 )
        glVertex3f( 0, h, 0 )
        glVertex3f( w, h, 0 )
        glVertex3f( w, 0, 0 )
        glVertex3f( 0, 0, 0 )
        glEnd()
        glEndList()
        
    def Draw( self, world ):
        glPushMatrix()
        glTranslate( *self.position )
        glUniform1i( world.uTextureSlotNumber_addr, self.textureSlotNumber )
        glUniform2f( world.uTextureSize_addr, *self.textureSize )
        glCallList( self.listNumber )
        glPopMatrix()
        

world = World( 1000, 800 )
digits = Texture( Image.open( 'digits.png' ) )
done = False
while not done:
    world.RenderFrame( digits )
    for event in pygame.event.get():
        # Press 'q' to quit or 's' to save a timestamped snapshot
        if event.type  == pygame.locals.QUIT: done = True
        elif event.type == pygame.locals.KEYUP and event.key in [ ord( 'q' ), 27 ]: done = True
        elif event.type == pygame.locals.KEYUP and event.key in [ ord( 's' ) ]:
            world.Capture().save( time.strftime( 'snapshot-%Y%m%d-%H%M%S.png' ) )
world.Close()

【问题讨论】:

标签: algorithm opengl printf glsl shader


【解决方案1】:

+1 有趣的问题。很好奇,所以我尝试对此进行编码。我需要使用数组,所以我选择了#version 420 core。我的应用程序正在渲染具有坐标&lt;-1,+1&gt; 的单个四边形覆盖屏幕。我正在使用我几年前创建的整个 ASCII 8x8 像素 32x8 字符字体纹理

顶点很简单:

//---------------------------------------------------------------------------
// Vertex
//---------------------------------------------------------------------------
#version 420 core
//---------------------------------------------------------------------------
layout(location=0) in vec4 vertex;
out vec2 pos;   // screen position <-1,+1>
void main()
    {
    pos=vertex.xy;
    gl_Position=vertex;
    }
//---------------------------------------------------------------------------

片段有点复杂:

//---------------------------------------------------------------------------
// Fragment
//---------------------------------------------------------------------------
#version 420 core
//---------------------------------------------------------------------------
in vec2 pos;                    // screen position <-1,+1>
out vec4 gl_FragColor;          // fragment output color
uniform sampler2D txr_font;     // ASCII 32x8 characters font texture unit
uniform float fxs,fys;          // font/screen resolution ratio
//---------------------------------------------------------------------------
const int _txtsiz=32;           // text buffer size
int txt[_txtsiz],txtsiz;        // text buffer and its actual size
vec4 col;                       // color interface for txt_print()
//---------------------------------------------------------------------------
void txt_decimal(float x)       // print float x into txt
    {
    int i,j,c;          // l is size of string
    float y,a;
    const float base=10;
    // handle sign
    if (x<0.0) { txt[txtsiz]='-'; txtsiz++; x=-x; }
     else      { txt[txtsiz]='+'; txtsiz++; }
    // divide to int(x).fract(y) parts of number
    y=x; x=floor(x); y-=x;
    // handle integer part
    i=txtsiz;                   // start of integer part
    for (;txtsiz<_txtsiz;)
        {
        a=x;
        x=floor(x/base);
        a-=base*x;
        txt[txtsiz]=int(a)+'0'; txtsiz++;
        if (x<=0.0) break;
        }
    j=txtsiz-1;                 // end of integer part
    for (;i<j;i++,j--)      // reverse integer digits
        {
        c=txt[i]; txt[i]=txt[j]; txt[j]=c;
        }
    // handle fractional part
    for (txt[txtsiz]='.',txtsiz++;txtsiz<_txtsiz;)
        {
        y*=base;
        a=floor(y);
        y-=a;
        txt[txtsiz]=int(a)+'0'; txtsiz++;
        if (y<=0.0) break;
        }
    txt[txtsiz]=0;  // string terminator
    }
//---------------------------------------------------------------------------
void txt_print(float x0,float y0)   // print txt at x0,y0 [chars]
    {
    int i;
    float x,y;
    // fragment position [chars] relative to x0,y0
    x=0.5*(1.0+pos.x)/fxs; x-=x0;
    y=0.5*(1.0-pos.y)/fys; y-=y0;
    // inside bbox?
    if ((x<0.0)||(x>float(txtsiz))||(y<0.0)||(y>1.0)) return;
    // get font texture position for target ASCII
    i=int(x);               // char index in txt
    x-=float(i);
    i=txt[i];
    x+=float(int(i&31));
    y+=float(int(i>>5));
    x/=32.0; y/=8.0;    // offset in char texture
    col=texture2D(txr_font,vec2(x,y));
    }
//---------------------------------------------------------------------------
void main()
    {
    col=vec4(0.0,1.0,0.0,1.0);  // background color
    txtsiz=0;
    txt[txtsiz]='F'; txtsiz++;
    txt[txtsiz]='l'; txtsiz++;
    txt[txtsiz]='o'; txtsiz++;
    txt[txtsiz]='a'; txtsiz++;
    txt[txtsiz]='t'; txtsiz++;
    txt[txtsiz]=':'; txtsiz++;
    txt[txtsiz]=' '; txtsiz++;
    txt_decimal(12.345);
    txt_print(1.0,1.0);
    gl_FragColor=col;
    }
//---------------------------------------------------------------------------

这是我的 CPU 侧制服:

    glUniform1i(glGetUniformLocation(prog_id,"txr_font"),0);
    glUniform1f(glGetUniformLocation(prog_id,"fxs"),(8.0)/float(xs));
    glUniform1f(glGetUniformLocation(prog_id,"fys"),(8.0)/float(ys));

xs,ys 是我的屏幕分辨率。单位 0 中的字体为 8x8

此处输出测试片段代码:

如果您的浮点精度由于硬件实现而降低,那么您应该考虑以不存在精度损失的十六进制打印(使用二进制访问)。以后可以将其转换为基于整数的十进制...

见:

[Edit2] 旧式 GLSL 着色器

我尝试移植到旧样式 GLSL 并且突然它起作用了(在它无法与存在的数组一起编译之前,但当我想到它时,我正在尝试 char[] 这是真正的原因)。

//---------------------------------------------------------------------------
// Vertex
//---------------------------------------------------------------------------
varying vec2 pos;   // screen position <-1,+1>
void main()
    {
    pos=gl_Vertex.xy;
    gl_Position=gl_Vertex;
    }
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// Fragment
//---------------------------------------------------------------------------
varying vec2 pos;                   // screen position <-1,+1>
uniform sampler2D txr_font;     // ASCII 32x8 characters font texture unit
uniform float fxs,fys;          // font/screen resolution ratio
//---------------------------------------------------------------------------
const int _txtsiz=32;           // text buffer size
int txt[_txtsiz],txtsiz;        // text buffer and its actual size
vec4 col;                       // color interface for txt_print()
//---------------------------------------------------------------------------
void txt_decimal(float x)       // print float x into txt
    {
    int i,j,c;          // l is size of string
    float y,a;
    const float base=10.0;
    // handle sign
    if (x<0.0) { txt[txtsiz]='-'; txtsiz++; x=-x; }
     else      { txt[txtsiz]='+'; txtsiz++; }
    // divide to int(x).fract(y) parts of number
    y=x; x=floor(x); y-=x;
    // handle integer part
    i=txtsiz;                   // start of integer part
    for (;txtsiz<_txtsiz;)
        {
        a=x;
        x=floor(x/base);
        a-=base*x;
        txt[txtsiz]=int(a)+'0'; txtsiz++;
        if (x<=0.0) break;
        }
    j=txtsiz-1;                 // end of integer part
    for (;i<j;i++,j--)      // reverse integer digits
        {
        c=txt[i]; txt[i]=txt[j]; txt[j]=c;
        }
    // handle fractional part
    for (txt[txtsiz]='.',txtsiz++;txtsiz<_txtsiz;)
        {
        y*=base;
        a=floor(y);
        y-=a;
        txt[txtsiz]=int(a)+'0'; txtsiz++;
        if (y<=0.0) break;
        }
    txt[txtsiz]=0;  // string terminator
    }
//---------------------------------------------------------------------------
void txt_print(float x0,float y0)   // print txt at x0,y0 [chars]
    {
    int i;
    float x,y;
    // fragment position [chars] relative to x0,y0
    x=0.5*(1.0+pos.x)/fxs; x-=x0;
    y=0.5*(1.0-pos.y)/fys; y-=y0;
    // inside bbox?
    if ((x<0.0)||(x>float(txtsiz))||(y<0.0)||(y>1.0)) return;
    // get font texture position for target ASCII
    i=int(x);               // char index in txt
    x-=float(i);
    i=txt[i];
    x+=float(int(i-((i/32)*32)));
    y+=float(int(i/32));
    x/=32.0; y/=8.0;    // offset in char texture
    col=texture2D(txr_font,vec2(x,y));
    }
//---------------------------------------------------------------------------
void main()
    {
    col=vec4(0.0,1.0,0.0,1.0);  // background color
    txtsiz=0;
    txt[txtsiz]='F'; txtsiz++;
    txt[txtsiz]='l'; txtsiz++;
    txt[txtsiz]='o'; txtsiz++;
    txt[txtsiz]='a'; txtsiz++;
    txt[txtsiz]='t'; txtsiz++;
    txt[txtsiz]=':'; txtsiz++;
    txt[txtsiz]=' '; txtsiz++;
    txt_decimal(12.345);
    txt_print(1.0,1.0);
    gl_FragColor=col;
    }
//---------------------------------------------------------------------------

【讨论】:

  • 这是令人印象深刻的作品!谢谢!出于某种原因,虽然我的一台机器的 GLSL >4.20(实际上是 4.30),但它的编译器不允许像 '-' 这样的字符文字(它说 ERROR: 0:22: '' : illegal character (') (0x27))。所以我不得不在整个过程中用相应的整数文字替换它们,但后来我能够让它工作。很酷!我应该提到我真的希望能够支持 1.20 版本的 GLSL 版本,但这不应该影响您的解决方案,尤其是核心十进制算法看起来也应该可以移植到那里。
  • 另外:我同意你关于二进制渲染的观点,并且很乐意这样做,因为自动化屏幕捕获然后解码一系列开关像素应该很容易。这实际上可能是一个更有用的通用解决方案。但是我什至可以在早期的 GLSL 版本中这样做吗?我知道FloatBitsToUint,但这仅适用于 3.30+ 版本。当您谈到“二进制访问”时,您是否想到了其他功能,如果有,它们是否可以在遗留系统上使用?我想这是一个完全独立的问题......
  • @jaz 我从来没有在 GLSL 中尝试过,但是对于 C++ 中的浮点二进制访问,我现在在 y.u 上使用 union { float f; DWORD u; } y; y.f=12.34; 是具有二进制访问权限的整数 ... 不需要指针,不需要强制转换。此外,核心 420 的唯一原因是使用了数组,我不知道他们是从哪个版本将它们添加到 GLSL 中的,但因为它可以工作,所以我保持原样......您可以避免使用带有一组变量的数组,但是代码看起来很乱......
  • 感谢提示:union(编辑:哦,等等,我看到你在谈论 C。GLSL 没有 union,事实上......)我会如果它存在的话,这个技巧也有其他一些用途。可以很好地为 v.1.20 编写数字算法 - 请参阅下面的答案。
  • @jaz 我已经做过并投了赞成票...还可以看看这个 Write your own implementation of math's floor function, C 的联合示例...在此之前我使用的是指针,但联合我更喜欢
【解决方案2】:

首先我想提一下,Spektre 的惊人解决方案几乎是完美的,甚至是文本输出的通用解决方案。我给了他的回答赞成。 作为替代方案,我提出了一种微创解决方案,并改进了问题的代码。

我不想隐瞒我研究过Spektre的解决方案并集成到我的解决方案中的事实。

// Assume that the texture to which uTextureSlotNumber refers contains
// a rendering of the digits '0123456789' packed together, such that
const vec2 startOfDigitsInTexture = vec2( 100, 125 ); // the lower-left corner of the first digit starts here and
const vec2 sizeOfDigit = vec2( 0.1, 0.2 ); // each digit spans this many pixels
const float nSpaces = 10.0; // assume we have this many digits' worth of space to render in

void RenderDigit( int strPos, int digit, vec2 pos )
{
    float testStrPos = pos.x / sizeOfDigit.x;
    if ( testStrPos >= float(strPos) && testStrPos < float(strPos+1) )
    {
        float start = sizeOfDigit.x * float(digit);
        vec2 textureSourcePosition = vec2( startOfDigitsInTexture.x + start + mod( pos.x, sizeOfDigit.x ),     startOfDigitsInTexture.y + pos.y );
        gl_FragColor = texture2D( uTextureSlotNumber, textureSourcePosition / uTextureSize );
    }
}

函数ValueToDigits 解释一个浮点数并用数字填充一个数组。 数组中的每个数字都在 (0, 9) 中。

const int MAX_DIGITS = 32;
int       digits[MAX_DIGITS];
int       noOfDigits = 0;
int       posOfComma = 0;

void Reverse( int start, int end )
{
    for ( ; start < end; ++ start, -- end )
    {
        int digit = digits[start];
        digits[start] = digits[end];
        digits[end] = digit;
    }
}

void ValueToDigits( float value )
{
    const float base = 10.0;
    int start = noOfDigits;

    value = abs( value );
    float frac = value; value = floor(value); frac -= value;

    // integral digits
    for ( ; value > 0.0 && noOfDigits < MAX_DIGITS; ++ noOfDigits )
    {
        float newValue = floor( value / base );
        digits[noOfDigits] = int( value - base * newValue );
        value = newValue;
    }
    Reverse( start, noOfDigits-1 );

    posOfComma = noOfDigits;

    // fractional digits
    for ( ; frac > 0.0 && noOfDigits < MAX_DIGITS; ++ noOfDigits )
    {
        frac *= base;
        float digit = floor( frac );
        frac -= digit;
        digits[noOfDigits] = int( digit );
    }
}

在您的原始函数中调用ValueToDigits 并找到当前片段的数字和纹理坐标。

void RenderDecimal( float value )
{
    // fill the array of digits with the floating point value
    ValueToDigits( value );

    // Render the digits
    vec2 pos = vFragCoordinate.xy - startOfDigitsInTexture;
    if( pos.x >= 0 && pos.x < sizeOfDigit.x * nSpaces && pos.y >= 0 && pos.y < sizeOfDigit.y  )
    {
        // render the digits
        for ( int strPos = 0; strPos < noOfDigits; ++ strPos )
            RenderDigit( strPos, digits[strPos], pos );
    }

    // Render the decimal point
    float testStrPos = pos.x / sizeOfDigit.x;
    float remainder = mod( pos.x, sizeOfDigit.x );
    if( ( testStrPos >= float(posOfComma) && testStrPos < float(posOfComma+1) && remainder / sizeOfDigit.x < 0.1 && abs( pos.y     ) / sizeOfDigit.y < 0.1 ) ||
        ( testStrPos >= float(posOfComma-1) && testStrPos < float(posOfComma) && remainder / sizeOfDigit.x > 0.9 && abs( pos.y     ) / sizeOfDigit.y < 0.1 ) )
    {
        gl_FragColor = texture2D( uTextureSlotNumber, ( startOfDigitsInTexture + sizeOfDigit * vec2( 1.5, 0.5 ) ) /     uTextureSize );
    }
}

【讨论】:

    【解决方案3】:

    这是我更新的片段着色器,可以放入我原来问题的列表中。它实现了 Spektre 提出的十进制数字查找算法,其方式甚至与我正在使用的旧版 GLSL 1.20 方言兼容。如果没有这种限制,Spektre 的解决方案当然会更加优雅和强大。

    varying vec2      vFragCoordinate;
    
    uniform vec2      uTextureSize;
    uniform sampler2D uTextureSlotNumber;
    
    float Digit( float x, int position, float base )
    {
        int i;
        float digit;
    
        if( position < 0 )
        {
            x = fract( x );
            for( i = -1; i >= position; i-- )
            {
                if( x <= 0.0 ) { digit = 0.0; break; }
                x *= base;
                digit = floor( x );
                x -= digit;
            }
        }
        else
        {
            x = floor( x );
            float prevx;
            for( i = 0; i <= position; i++ )
            {
                if( x <= 0.0 ) { digit = 0.0; break; }
                prevx = x;
                x = floor( x / base );
                digit = prevx - base * x;
            }
        }
        return digit;
    }
    
    float OrderOfMagnitude( float x )
    {
        return x == 0.0 ? 0.0 : floor( log( abs( x ) ) / log( 10.0 ) );
    }
    void RenderDecimal( float value )
    {
        // Assume that the texture to which uTextureSlotNumber refers contains
        // a rendering of the digits '0123456789' packed together, such that
        const vec2 startOfDigitsInTexture = vec2( 0, 0 ); // the lower-left corner of the first digit starts here and
        const vec2 sizeOfDigit = vec2( 100, 125 ); // each digit spans this many pixels
        const float nSpaces = 10.0; // assume we have this many digits' worth of space to render in
    
        value = abs( value );
        vec2 pos = vFragCoordinate - startOfDigitsInTexture;
        float dpstart = max( 0.0, OrderOfMagnitude( value ) );
        int decimal_position = int( dpstart - floor( pos.x / sizeOfDigit.x ) );
        float remainder = mod( pos.x, sizeOfDigit.x );
    
        if( pos.x >= 0.0 && pos.x < sizeOfDigit.x * nSpaces && pos.y >= 0.0 && pos.y < sizeOfDigit.y  )
        {
            float digit_value = Digit( value, decimal_position, 10.0 );
            vec2 textureSourcePosition = vec2( startOfDigitsInTexture.x + remainder + digit_value * sizeOfDigit.x, startOfDigitsInTexture.y + pos.y );
            gl_FragColor = texture2D( uTextureSlotNumber, textureSourcePosition / uTextureSize );
        }
    
        // Render the decimal point
        if( ( decimal_position == -1 && remainder / sizeOfDigit.x < 0.1 && abs( pos.y ) / sizeOfDigit.y < 0.1 ) ||
            ( decimal_position ==  0 && remainder / sizeOfDigit.x > 0.9 && abs( pos.y ) / sizeOfDigit.y < 0.1 ) )
        {
            gl_FragColor = texture2D( uTextureSlotNumber, ( startOfDigitsInTexture + sizeOfDigit * vec2( 1.5, 0.5 ) ) / uTextureSize );
        }
    }
    
    void main(void)
    {
        gl_FragColor = texture2D( uTextureSlotNumber, vFragCoordinate / uTextureSize );
        RenderDecimal( 2.5 ); // for current demonstration purposes, just a constant
    }
    

    【讨论】:

      猜你喜欢
      • 2014-03-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-08
      • 2014-11-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多