ฉันจะนำ GPU มาสร้างสกินที่น่าเชื่อถือใน Android ได้อย่างไร?


11

ฉันพยายามทำให้ตัวละครน่าสนใจทำงานบน Android

แนวคิดนี้ค่อนข้างวานิลลา: ฉันมีเมทริกซ์ที่น่าสนใจและด้วยจุดสุดยอดแต่ละอันฉันจะส่งดัชนีเมทริกซ์ได้ถึงสี่และน้ำหนักที่สอดคล้องกันสี่ประการ ฉันรวมพวกมันไว้ในส่วนยอดและนำไปใช้กับจุดสุดยอดแต่ละอัน

นี่คือสิ่งที่ฉันกำลังทำในจุดสุดยอดในเกม iOS ของฉัน (ไม่ต้องสนใจมาตรฐาน):

attribute vec4 in_pos;
attribute vec4 in_normal;
attribute vec2 in_texture_coords;
attribute vec4 in_bone_index;
attribute vec4 in_bone_weight;

varying vec2 fs_texture_coords;

uniform mat4 world_view_projection;
uniform mat4 bones[@bind_matrix_count];

void main()
{
    // Skinning
    vec4 transformed_pos =
        ((in_pos * bones[int(in_bone_index.x)]) * in_bone_weight.x) +
        ((in_pos * bones[int(in_bone_index.y)]) * in_bone_weight.y) +
        ((in_pos * bones[int(in_bone_index.z)]) * in_bone_weight.z) +
        ((in_pos * bones[int(in_bone_index.w)]) * in_bone_weight.w);

    gl_Position = world_view_projection * transformed_pos;
    fs_texture_coords = in_texture_coords;
}

และใช้งานได้ดีทีเดียว อย่างไรก็ตามด้วยรหัสเดียวกันใน Android ในอุปกรณ์บางเครื่อง (โดยเฉพาะ Nexus 7 2013) คุณจะไม่สามารถเข้าถึงuniformดัชนีที่ไม่คงที่ได้ คุณไม่สามารถทำสิ่งนี้ได้:

bones[int(in_bone_index.w)]

เพราะbones[some_non_constant]ได้รับการประเมินเสมอว่าbones[0]ซึ่งไม่สนุกเลย สิ่งที่แย่ที่สุดคือคอมไพเลอร์ shader รวบรวมสิ่งนี้อย่างมีความสุข

ผู้ชายคนนี้ดูเหมือนจะมีปัญหาเดียวกัน เขาแก้ไขมันด้วยการเข้าถึงเครื่องแบบในฐานะเวกเตอร์แทนที่จะเป็นเมทริกซ์ ฉันทำแบบเดียวกันและในความเป็นจริงมันก็ใช้งานได้!

attribute vec4 in_pos;
attribute vec4 in_normal;
attribute vec2 in_texture_coords;
attribute vec4 in_bone_index;
attribute vec4 in_bone_weight;

varying vec2 fs_texture_coords;

uniform mat4 world_view_projection;
uniform vec4 bones[@bind_matrix_count * 4]; // four vec4's for each matrix

void main()
{
    // Skinning
    mat4 skin_0 = mat4(
        bones[4 * int(in_bone_index.x) + 0],
        bones[4 * int(in_bone_index.x) + 1],
        bones[4 * int(in_bone_index.x) + 2],
        bones[4 * int(in_bone_index.x) + 3]);
    mat4 skin_1 = mat4(
        bones[4 * int(in_bone_index.y) + 0],
        bones[4 * int(in_bone_index.y) + 1],
        bones[4 * int(in_bone_index.y) + 2],
        bones[4 * int(in_bone_index.y) + 3]);
    mat4 skin_2 = mat4(
        bones[4 * int(in_bone_index.z) + 0],
        bones[4 * int(in_bone_index.z) + 1],
        bones[4 * int(in_bone_index.z) + 2],
        bones[4 * int(in_bone_index.z) + 3]);
    mat4 skin_3 = mat4(
        bones[4 * int(in_bone_index.w) + 0],
        bones[4 * int(in_bone_index.w) + 1],
        bones[4 * int(in_bone_index.w) + 2],
        bones[4 * int(in_bone_index.w) + 3]);
    vec4 transformed_pos =
        ((in_pos * skin_0) * in_bone_weight.x) +
        ((in_pos * skin_1) * in_bone_weight.y) +
        ((in_pos * skin_2) * in_bone_weight.z) +
        ((in_pos * skin_3) * in_bone_weight.w);

    gl_Position = world_view_projection * transformed_pos;
    fs_texture_coords = in_texture_coords;
}

แต่ฉันคิดว่านี่เป็นโอกาส uniforms ไม่ได้ตั้งใจให้เข้าถึงแบบสุ่มดังนั้นฉันจึงกลัวว่า "เทคนิค" นี้จะไม่ทำงานในทุกอุปกรณ์

ผู้ชายคนนี้ผ่านการฝึกอบรมของเขาเป็นพื้นผิวซึ่งเป็นความคิดที่ดีงาม ฉันสร้างพื้นผิว 4x32 OES_texture_float โดยที่เท็กเซลแต่ละตัวเป็นแถวเมทริกซ์และแต่ละแถวของพื้นผิวนั้นเป็นเมทริกซ์ทั้งหมด ฉันเข้าใช้งานเช่นนี้:

attribute vec4 in_pos;
attribute vec4 in_normal;
attribute vec2 in_texture_coords;
attribute vec4 in_bone_index;
attribute vec4 in_bone_weight;

varying vec2 fs_texture_coords;

uniform mat4 world_view_projection; // A texture!
uniform sampler2D bones;

void main()
{
    // Skinning
    mat4 bone0 = mat4(
        texture2D(bones, vec2(0.00, in_bone_index.x / 32.0)),
        texture2D(bones, vec2(0.25, in_bone_index.x / 32.0)),
        texture2D(bones, vec2(0.50, in_bone_index.x / 32.0)),
        texture2D(bones, vec2(0.75, in_bone_index.x / 32.0)));
    mat4 bone1 = mat4(
        texture2D(bones, vec2(0.00, in_bone_index.y / 32.0)),
        texture2D(bones, vec2(0.25, in_bone_index.y / 32.0)),
        texture2D(bones, vec2(0.50, in_bone_index.y / 32.0)),
        texture2D(bones, vec2(0.75, in_bone_index.y / 32.0)));
    mat4 bone2 = mat4(
        texture2D(bones, vec2(0.00, in_bone_index.z / 32.0)),
        texture2D(bones, vec2(0.25, in_bone_index.z / 32.0)),
        texture2D(bones, vec2(0.50, in_bone_index.z / 32.0)),
        texture2D(bones, vec2(0.75, in_bone_index.z / 32.0)));
    mat4 bone3 = mat4(
        texture2D(bones, vec2(0.00, in_bone_index.w / 32.0)),
        texture2D(bones, vec2(0.25, in_bone_index.w / 32.0)),
        texture2D(bones, vec2(0.50, in_bone_index.w / 32.0)),
        texture2D(bones, vec2(0.75, in_bone_index.w / 32.0)));
    vec4 transformed_pos =
        ((in_pos * bone0) * in_bone_weight.x) +
        ((in_pos * bone1) * in_bone_weight.y) +
        ((in_pos * bone2) * in_bone_weight.z) +
        ((in_pos * bone3) * in_bone_weight.w);

    gl_Position = world_view_projection * transformed_pos;
    fs_texture_coords = in_texture_coords;
}

ในความเป็นจริงมันใช้งานได้ดี ... จนกระทั่งฉันลองใช้กับ Galaxy Note 2 ของฉันในครั้งนี้ผู้รวบรวมบ่นว่าฉันไม่สามารถใช้texture2Dกับจุดสุดยอดได้!

ดังนั้นสิ่งที่ฉันกำลังทำคือการตรวจสอบว่า GPU รองรับการเข้าถึงพื้นผิวบน Shader ที่ยอดและถ้ามันรองรับ OES_texture_float ถ้าเป็นเช่นนั้นฉันใช้วิธีพื้นผิว หากไม่เป็นเช่นนั้นฉันใช้วิธีเวกเตอร์

อย่างไรก็ตามวิธีเนื้อสัมผัสไม่พร้อมใช้งานบนแพลตฟอร์มทั้งหมดและวิธีการแบบเวกเตอร์ทำงานโดยบังเอิญ ฉันต้องการทราบว่ามีวิธีที่จะผ่านการฝึกอบรมที่น่าสนใจของฉันไปยัง Shader ที่ซึ่งทำงานบนอุปกรณ์ทั้งหมดได้อย่างน่าเชื่อถือ

ฉันสามารถมีความต้องการขั้นต่ำของระบบปฏิบัติการที่สมเหตุสมผลเช่น Android 4.1 ขึ้นไป แต่ฉันต้องการมีวิธีแก้ไขปัญหาที่ทำงานบนอุปกรณ์ทั้งหมดที่ตรงตามข้อกำหนดเหล่านั้น


ดีฉันไม่สามารถคิดทางเลือกได้ TBH ฉันคิดว่าทางออกที่ดีที่สุดของคุณคือการใช้ทั้งสองเทคนิคขึ้นอยู่กับว่ามีเทคนิคใดที่พร้อมใช้งานหากไม่มี avaible อย่าใช้ GPU skinning เพียงใช้ทางเลือก CPU skinning ?)
concept3d

@ concept3d: ฉันไม่รู้อาจจะมีบางส่วนที่รับประกันว่าจะมีอยู่บน Android 4.1 ขึ้นไปซึ่งออกแบบมาเพื่อแก้ปัญหานี้หรือทำสิ่งที่แตกต่างทางแนวคิดกับผลลัพธ์เดียวกัน ฉันพิจารณาตัวเองค่อนข้างเชี่ยวชาญในแนวคิดทั่วไปของหัวข้อมากมาย แต่ความรู้ของฉันเกี่ยวกับแพลตฟอร์ม Android นั้น จำกัด มากและฉันคิดว่าอาจมีวิธีแก้ปัญหาทางเทคนิคล้วนๆสำหรับปัญหานี้
แพนด้า Pajama

ดังนั้นนี่อาจเป็นเหตุผลว่าทำไมฉันถึงไม่สามารถสร้างสกินที่น่าทึ่งให้กับ Nexus 7 ของฉัน: / ขอบคุณคำถามของคุณเปิดตาของฉัน!
async

คำตอบ:


4

นี่เป็นพฤติกรรมที่ไม่สอดคล้องกับ Nexus 7 (Adreno GPU) คุณพูดว่า "เครื่องแบบไม่ได้ตั้งใจให้เข้าถึงแบบสุ่ม" แต่อ้างอิงจากภาคผนวก A ของข้อมูลจำเพาะ :

เครื่องแบบ (ไม่รวมแซมเพลอร์)

ในจุดสุดยอด shader การสนับสนุนสำหรับการทำดัชนีอาเรย์ทุกรูปแบบได้รับคำสั่ง ในแฟรกเมนต์ shader การสนับสนุนสำหรับการจัดทำดัชนีจะได้รับคำสั่งสำหรับการแสดงดัชนีคงที่เท่านั้น

เสียงจากการอภิปรายที่นี่ว่าข้อผิดพลาดนี้ใช้เฉพาะกับอาร์เรย์เมทริกซ์ที่เหมือนกันดังนั้นการทำงานโดยใช้เวกเตอร์มีแนวโน้มที่จะทำงานได้อย่างน่าเชื่อถือและพกพาไปยัง GPU อื่น ๆ (ฉันรู้ว่าการจัดทำดัชนีแบบสุ่ม


อืมฉันเดาว่ามันดูเหมือนว่าถูก ฉันคิดว่าฉันได้อ่านหัวข้อก่อนหน้านี้แล้ว แต่ไม่มี Qualcomm จากการยืนยันปัญหานี้
ชุดนอนแพนด้า
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.