Il gradiente di colore del vertice non è lineare

Aug 21 2020

Sto dipingendo il cubo predefinito attraverso la colorazione dei vertici. Dipingo i quattro vertici inferiori come (0,25, 0,25, 0,25) e i quattro vertici superiori come (0,75, 0,75, 0,75).

Quando controllo il risultato in modalità VERTEX PAINT (usando il tasto di scelta rapida S per ottenere il colore), la parte inferiore del cubo è effettivamente (0,25, 0,25, 0,25) e la parte superiore è (0,75, 0,75, 0,75).

Tuttavia, il punto medio del cubo non è (0,5, 0,5, 0,5), è ~ (0,56, 0,56, 0,56). In qualche modo il gradiente non è lineare. Non penso che sia un problema con sRGB vs Linear, perché altrimenti anche i valori inferiore e superiore sarebbero sbagliati, ma sono solo i valori intermedi. Allora qual è il problema qui? Ho davvero bisogno di un gradiente lineare.

MODIFICARE. problema correlato:https://developer.blender.org/T71835 Questo dice che ha qualcosa a che fare con il modo in cui vengono memorizzati i colori dei vertici (apparentemente sRGB a 8 bit).

Codice per riprodurre questo problema:

"""Script that checks vertex color interpolation error on the default cube.

run command: `blender --python gradient_error.py` (opens blender)
"""

import bpy

# Set up scene
sce = bpy.context.scene

# Select cube
scene = bpy.context.scene
for ob in scene.objects:
    ob.select_set(False)
    if ob.name == 'Cube':
        ob.select_set(True)
obj = bpy.context.view_layer.objects.active

# Get cube data
mesh = obj.data
vert_list = mesh.vertices

# Get the color map
if mesh.vertex_colors:
    color_map = mesh.vertex_colors.active
else:
    color_map = mesh.vertex_colors.new()

# apply colors
i = 0
for poly in mesh.polygons:
    for idx in poly.loop_indices: #vertices
        loop = mesh.loops[idx]
        v = vert_list[loop.vertex_index]
    
        z_coord = v.co.z

        # Paint bottom 4 vertices 0.25, top 4 vertices 0.75.
        if z_coord < 0:
            color_map.data[i].color = (0.25, 0.25, 0.25, 0)
        else:
            color_map.data[i].color = (0.75, 0.75, 0.75, 0)
        i += 1

# Give it this new material
mat = bpy.data.materials.new('vcolor_material')

# Deactivate shadows
mat.shadow_method = 'NONE'

# Apply material
mesh.materials.append(mat)

# Set Flat lighting and show Vertex Coloring
my_areas = bpy.context.workspace.screens[0].areas
my_shading = 'WIREFRAME'
for area in my_areas:
    for space in area.spaces:
        if space.type == 'VIEW_3D':
            space.shading.light = "FLAT"
            space.shading.color_type = "VERTEX"

bpy.ops.object.mode_set(mode='VERTEX_PAINT')

print("\nRan succesfully.\n")

Risposte

5 RichSedman Aug 25 2020 at 16:03

Il gradiente è lineare ma non nello spazio colore che ti aspetti, come evidenziato da quanto segue:

Qui il nodo Attribute rende disponibile allo shader il Vertex Color (Col). Questo è suddiviso in componenti RGB separati e confrontato con la coordinata "X" generata (che va da 0,0 a sinistra a 1,0 a destra del cubo).

Il gradiente è lineare ma i punti finali non sono come ci si aspetta (cioè, la linea della griglia verticale blu è a X = 0,5 e la linea del gradiente va da circa 0,05 in basso fino a circa 0,525 in alto). Ciò è dovuto alla conversione dello spazio colore sRGB.

I tuoi valori di 0,25 e 0,75 si riferiscono a quei valori 'assoluti' di circa 0,05 e 0,525 quando convertiti da sRGB nella luminosità 'vera': lo spazio colore sRGB ha lo scopo di sfruttare al massimo una rappresentazione intera a 0 bit della luminosità da realizzare è il più visivamente diversificato possibile, quindi i valori all'inizio / alla fine dell'intervallo e compressi / espansi per adattarsi a ciò che sarebbe visibile quando visualizzato su uno schermo all'occhio umano.

Vedere https://en.wikipedia.org/wiki/SRGB e notare la linea rossa nel grafico seguente:

Usando i valori degli assi in basso e a destra del grafico puoi vedere che 0,75 sull'asse x converte a circa 0,5 in 'intensità' - il tuo codice Python sta applicando il valore sRGB di 0,75 e produce un 'intensità' di circa 0,525 per il limite superiore. Allo stesso modo, il valore inferiore di 0,25 viene convertito all'estremità inferiore del grafico in un valore di "intensità" molto inferiore. Quell'intensità è ciò che viene interpolato linearmente per produrre il gradiente di colore del vertice risultante, non i valori sRGB originali.

1 uvnoob Aug 25 2020 at 13:32

Se hai solo bisogno di dati in scala di grigi da utilizzare nei nodi shader, ecco una brutta soluzione alternativa: usa invece il canale alfa. Nel tuo script ho impostato i valori alfa rispettivamente su 0,25 e 0,75 e l'utilizzo di un greater than 0.5nodo ha confermato visivamente che il canale alfa è interpolato linearmente.

Forse potrebbero essere usati anche altri attributi, come UV.

Temo che una spiegazione conica sia al di là di me, ma ecco un thread che discute il problema. La soluzione alternativa si basa sulla risposta collegata

https://github.com/KhronosGroup/glTF-Blender-IO/issues/542#issuecomment-569361267