Как привязать текстуру к 3D me sh в Kivy? - PullRequest
1 голос
/ 19 февраля 2020

Я пытаюсь взять пример 3D вращающейся обезьяны Киви и применить к нему текстуру.

Кажется, что метод графического контекста Киви "BindTexture() метод должен сделать свое дело, но это, похоже, не имеет никакого эффекта. Я изменил пример, включив в него метод "BindTexture ()" в методе init (). Я также изменил код включения шейдера в текстуре.

Кажется, что я ' Нам удалось передать координаты текстуры в шейдер просто отлично, потому что когда я рендеринг использую только координаты текстуры в качестве значений цвета (используя строку комментария рядом с концом кода шейдера), я получаю разноцветный объект. Однако, когда я размещаю эту текстуру координаты внутри вызова texture2D (), объект отображается без текстуры вообще.

Согласно BindTexture docs:

Инструкция BindTexture будет свяжите текстуру и включите GL_TEXTURE_2D для последующего рисования.

Так что, похоже, это должно быть довольно просто, но я явно скучаю по чему-то Хин.

Я новичок в Kivy и OpenGL. Одна из вещей, с которой я действительно борюсь, это как передать значения во входные переменные в шейдерном коде GL. Например, в моем коде шейдера переменная "texture0" используется для рассматриваемой текстуры - однако я не уверен, как это будет отображаться в моей текстуре. На основании некоторых примеров, которые я видел, например, мультитекстура Киви , кажется, что «BindTexture ()» должен позаботиться об этом, но мне немного неловко, что все это похоже на черных магов c мне прямо сейчас.

main.py:

from kivy.clock import Clock
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.graphics import RenderContext, Color, Rectangle, BindTexture

class Renderer(Widget):
    def __init__(self, **kwargs):
        self.canvas = RenderContext(compute_normal_mat=True)
        # self.canvas.shader.source = resource_find('default.glsl')
        self.canvas.shader.source = resource_find('simple.glsl')
        self.scene = ObjFile(resource_find("monkey.obj"))
        BindTexture(source='mtexture2.png')


        super(Renderer, self).__init__(**kwargs)
        with self.canvas:
            self.cb = Callback(self.setup_gl_context)
            PushMatrix()
            self.setup_scene()
            PopMatrix()
            self.cb = Callback(self.reset_gl_context)
            BindTexture(source='mtexture2.png')


        self.canvas['texture0'] = 0

        Clock.schedule_interval(self.update_glsl, 1 / 60.)

    def setup_gl_context(self, *args):
        glEnable(GL_DEPTH_TEST)

    def reset_gl_context(self, *args):
        glDisable(GL_DEPTH_TEST)

    def update_glsl(self, delta):
        asp = self.width / float(self.height)
        proj = Matrix().view_clip(-asp, asp, -1, 1, 1, 100, 1)
        # Color(1,0,0)
        self.canvas['projection_mat'] = proj
        self.canvas['diffuse_light'] = (1.0, 0.0, 0.8)
        self.canvas['ambient_light'] = (0.1, 0.1, 0.1)
        self.rot.angle += delta * 100


    def setup_scene(self):
        Color(1, 1, 1)
        PushMatrix()
        Translate(0, -2.5, -8)
        self.rot = Rotate(1, 0, 1, 0)
        UpdateNormalMatrix()
        for m in list(self.scene.objects.values()):
            self.mesh = Mesh(
                vertices=m.vertices,
                indices=m.indices,
                fmt=m.vertex_format,
                mode='triangles',
            )
        PopMatrix()


class RendererApp(App):
    def build(self):
        return Renderer()


if __name__ == "__main__":
    RendererApp().run()

Код шейдера "simple.glsl":

/* simple.glsl

simple diffuse lighting based on laberts cosine law; see e.g.:
    http://en.wikipedia.org/wiki/Lambertian_reflectance
    http://en.wikipedia.org/wiki/Lambert%27s_cosine_law
*/
---VERTEX SHADER-------------------------------------------------------
#ifdef GL_ES
    precision highp float;
#endif


/* Vertex attributes/inputs, defined in MeshData Object */
attribute vec3  v_pos;
attribute vec3  v_normal;
attribute vec2  v_tc0;

/* Outputs to the fragment shader */
varying vec2 tex_coord0;
varying vec4 normal_vec;
varying vec4 vertex_pos;

uniform mat4 modelview_mat;
uniform mat4 projection_mat;

void main (void) {
    //compute vertex position in eye_space and normalize normal vector
    vec4 pos = modelview_mat * vec4(v_pos,1.0);
    vertex_pos = pos;
    normal_vec = vec4(v_normal,0.0);
    gl_Position = projection_mat * pos;

    tex_coord0 = v_tc0;
}


---FRAGMENT SHADER-----------------------------------------------------
#ifdef GL_ES
    precision highp float;
#endif

/* Outputs from Vertex Shader */
varying vec4 normal_vec;
varying vec4 vertex_pos;
varying vec2 tex_coord0;

uniform sampler2D texture0;

uniform mat4 normal_mat;

void main (void){
    //correct normal, and compute light vector (assume light at the eye)
    vec4 v_normal = normalize( normal_mat * normal_vec ) ;
    vec4 v_light = normalize( vec4(0,0,0,1) - vertex_pos );
    //reflectance based on lamberts law of cosine
    float theta = clamp(dot(v_normal, v_light), 0.0, 1.0);
    //gl_FragColor = vec4(theta, theta, theta, 1.0)*vec4(tex_coord0, 1, 1);
    gl_FragColor = vec4(theta, theta, theta, 1.0)*texture2D(texture0, tex_coord0);
}

1 Ответ

1 голос
/ 19 февраля 2020

Точкой привязки между объектом текстуры и униформой выборки текстуры является единица текстуры. Объект текстуры привязан к текстурному блоку, а индекс текстурного блока установлен в единицу сэмплера текстуры. Я понятия не имею, как это работает, используя kivy . Начальное значение сэмплера равно 0. Если текстура связана с единицей текстуры 0, она должна работать. Текстура привязана к единице текстуры с помощью BindTexture(source='mtexture2.png'), а единица текстуры присвоена униформе сэмплера с помощью self.canvas['texture0'] = 0.

Кажется, есть проблема при использовании единицы текстуры 0. Используйте единицу текстуры 1 вместо этого.
Привязать текстуру в setup_scene и установить значение равным сэмплеру в update_glsl:

class Renderer(Widget):
    # [...]

    def update_glsl(self, delta):
        asp = self.width / float(self.height)
        proj = Matrix().view_clip(-asp, asp, -1, 1, 1, 100, 1)
        self.canvas['texture0'] = 1 # <------------------------------  
        self.canvas['projection_mat'] = proj
        self.canvas['diffuse_light'] = (1.0, 0.0, 0.8)
        self.canvas['ambient_light'] = (0.1, 0.1, 0.1)
        self.rot.angle += delta * 100

    def setup_scene(self):
        BindTexture(source='mtexture2.png', index=1) # <-------------
        Color(1, 1, 1)
        PushMatrix()
        Translate(0, -2.5, -8)
        self.rot = Rotate(1, 0, 1, 0)
        UpdateNormalMatrix()
        for m in list(self.scene.objects.values()):
            self.mesh = Mesh(
                vertices=m.vertices,
                indices=m.indices,
                fmt=m.vertex_format,
                mode='triangles',
            )
        PopMatrix()

Еще одна проблема заключается в том, что «monkey.obj» не имеет координат текстуры ( vt записей). Таким образом, атрибут координаты текстуры по умолчанию (0, 0) для всех вершин.
Эмулируйте координаты текстуры по вектору нормали в вершинном шейдере. Например:

void main (void) {
    // [...]

    tex_coord0 = v_normal.xz * 0.5 + 0.5;
}

...