algorithm opengl printf glsl

algorithm - ¿Convertir números de coma flotante a dígitos decimales en GLSL?



opengl printf (3)

+1 por problema interesante. Tenía curiosidad, así que intenté codificar esto. Necesito el uso de matrices, así que elegí #version 420 core . Mi aplicación muestra una pantalla de cobertura cuádruple simple con coordenadas <-1,+1> . Estoy usando la textura de fuente ASCII 8x8 píxeles 32x8 caracteres que creé hace algunos años:

El vértice es simple:

//--------------------------------------------------------------------------- // 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; } //---------------------------------------------------------------------------

El fragmento es un poco más complicado:

//--------------------------------------------------------------------------- // 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; } //---------------------------------------------------------------------------

Aquí mis uniformes laterales de 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));

donde xs,ys es mi resolución de pantalla. La fuente es 8x8 en la unidad 0

Aquí salida para el código de fragmento de prueba:

Si la precisión de su punto flotante disminuye debido a la implementación de HW, entonces debería considerar imprimir en hexadecimal donde no hay pérdida de precisión (usando acceso binario). Eso podría convertirse en base decadica en enteros más tarde ...

ver:

  • conversión de cadena hex2dec en matemática entera

[Edit2] sombreadores GLSL de estilo antiguo

Traté de portar al GLSL de estilo antiguo y de repente funciona (antes no se compilaba con las matrices presentes, pero cuando pienso en ello estaba probando char[] que era la verdadera razón).

//--------------------------------------------------------------------------- // 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; } //---------------------------------------------------------------------------

Como otros han discutido , GLSL carece de cualquier tipo de depuración de printf. Pero a veces realmente quiero examinar los valores numéricos mientras depuro mis sombreadores.

He estado tratando de crear una herramienta de depuración visual. Descubrí que es posible renderizar una serie arbitraria de dígitos con bastante facilidad en un sombreador, si trabaja con un sampler2D en el que los dígitos 0123456789 se han representado en el monoespacio. Básicamente, solo haces malabares con tu coordenada x.

Ahora, para usar esto para examinar un número de coma flotante, necesito un algoritmo para convertir un float a una secuencia de dígitos decimales, como puede encontrar en cualquier implementación de printf . Desafortunadamente, hasta donde entiendo el tema, estos algoritmos parecen necesitar volver a representar el número de coma flotante en un formato de mayor precisión, y no veo cómo esto será posible en GLSL, donde parece que tengo solo float 32 bits disponibles. Por esta razón, creo que esta pregunta no es un duplicado de ninguna pregunta general sobre "cómo funciona printf", sino más bien específicamente sobre cómo se puede hacer que estos algoritmos funcionen bajo las restricciones de GLSL. He visto esta pregunta y respuesta , pero no tengo idea de lo que está sucediendo allí.

Los algoritmos que he probado no son muy buenos. Mi primer intento, marcado como Versión A (comentado), parecía bastante malo: para tomar tres ejemplos aleatorios, RenderDecimal(1.0) presentado como 1.099999702 , RenderDecimal(2.5) me dio 2.599999246 y RenderDecimal(2.6) salió como 2.699999280 . Mi segundo intento, marcado como Versión B, parecía un poco mejor: 1.0 y 2.6 salen bien, pero RenderDecimal(2.5) todavía no coincide con un redondeo aparente de los 5 con el hecho de que el residual es 0.099... El resultado aparece como 2.599000022 .

Mi ejemplo mínimo / completo / verificable, a continuación, comienza con un breve código GLSL 1.20, y luego he elegido Python 2.x para el resto, solo para compilar los sombreadores y cargar y renderizar las texturas. Requiere los paquetes de terceros pygame, numpy, PyOpenGL y PIL. Tenga en cuenta que Python es en realidad una plantilla repetitiva y podría reescribirse trivialmente (aunque tediosamente) en C o cualquier otra cosa. Solo el código GLSL en la parte superior es crítico para esta pregunta, y por esta razón no creo que las etiquetas python o python 2.x sean útiles.

Requiere que la siguiente imagen se guarde como 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()


Aquí está mi sombreador de fragmentos actualizado, que se puede colocar en la lista de mi pregunta original. Implementa el algoritmo de búsqueda de dígitos decimales que Spektre propuso, de una manera que incluso es compatible con el dialecto GLSL 1.20 heredado que estoy usando. Sin esa restricción, la solución de Spektre es, por supuesto, mucho más elegante y poderosa.

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 }


En primer lugar, quiero mencionar que la increíble solución de Spektre es casi perfecta y aún más una solución general para la salida de texto. Le di una respuesta a su respuesta. Como alternativa, presento una solución mínimamente invasiva y mejoro el código de la pregunta.

No quiero ocultar el hecho de que he estudiado la solución de Spektre y me he integrado a mi solución.

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

La función ValueToDigits interpreta un número de coma flotante y llena una matriz con los dígitos. Cada número en la matriz está en ( 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 ); } }

Llame a ValueToDigits en su función original y encuentre las coordenadas de dígitos y texturas para el fragmento actual.

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