मैं Android में GPU स्किनिंग को मज़बूती से कैसे लागू कर सकता हूँ?


11

मैं एंड्रॉइड पर काम करने वाले चरित्र स्किनिंग प्राप्त करने की कोशिश कर रहा हूं।

यह विचार काफी वैनिला है: मेरे पास मेरी स्किनिंग मैट्रिसेस हैं, और प्रत्येक शीर्ष के साथ मैं चार मैट्रिक्स इंडेक्स और चार संबंधित वेट भेजती हूं। मैं उन्हें वर्टेक्स शेडर में योग करता हूं, और उन्हें प्रत्येक वर्टेक्स पर लागू करता हूं।

यह वही है जो मैं अपने गेम के 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;
}

वास्तव में, यह बहुत अच्छा काम किया ... जब तक मैंने इसे अपने गैलेक्सी नोट 2 पर आज़माया नहीं। इस बार कंपाइलर ने शिकायत की कि मैं texture2Dवर्टेक्सर शेडर पर उपयोग नहीं कर सकता !

तो मैं क्या कर रहा हूँ जाँच कर रहा है कि क्या GPU वर्टेक्स शैडर पर बनावट एक्सेस का समर्थन करता है और यदि यह OES_texture_bloat का समर्थन करता है। यदि ऐसा होता है, तो मैं बनावट दृष्टिकोण का उपयोग कर रहा हूं। यदि ऐसा नहीं होता है, तो मैं वेक्टर दृष्टिकोण का उपयोग कर रहा हूं।

हालाँकि, बनावट दृष्टिकोण सभी प्लेटफार्मों पर उपलब्ध नहीं है, और वेक्टर दृष्टिकोण संयोग से काम कर रहा है। मैं यह जानना चाहूंगा कि क्या मेरी स्किनिंग मेट्रिसेस को वर्टेक्स शॉडर में पास करने का कोई तरीका है, जो सभी उपकरणों के लिए मज़बूती से काम करता है।

मुझे एंड्रॉइड 4.1+ की तरह न्यूनतम उचित ओएस आवश्यकताएं हो सकती हैं, लेकिन मैं एक समाधान चाहता हूं जो सभी उपकरणों पर काम करता है जो उन आवश्यकताओं को पूरा करते हैं।


खैर, मैं विकल्पों के बारे में नहीं सोच सकता, टीबीएच मुझे लगता है कि आपकी सबसे अच्छी शर्त दोनों तकनीकों का उपयोग करना है जिसके आधार पर कोई भी उपलब्ध है, अगर न तो avaible सीपीयू स्किनिंग कार्यान्वयन के लिए जीपीयू स्किनबैकिंग का उपयोग नहीं करते हैं (शायद कम विस्तृत मॉडल के साथ ?)।
कॉन्सेप्ट

@ concept3d: मुझे नहीं पता, शायद कुछ विस्तार जो एंड्रॉइड 4.1+ पर मौजूद होने की गारंटी है, जो इस समस्या को हल करने के लिए डिज़ाइन किया गया है, या समान परिणाम के साथ वैचारिक रूप से कुछ अलग कर रहा है। मैं अपने आप को बहुत सारे विषयों की सामान्य अवधारणाओं में काफी कुशल मानता हूं, लेकिन एंड्रॉइड प्लेटफॉर्म के बारे में मेरा ज्ञान बहुत सीमित है, और मैं सोच रहा था कि इस समस्या का एक विशुद्ध रूप से तकनीकी समाधान हो सकता है।
पांडा पजामा

तो शायद यही कारण है कि मैं अपने Nexus 7 पर काम करने के लिए स्किनिंग नहीं कर पाया। 7. / धन्यवाद, आपके सवाल ने मेरी आँखें खोल दीं!
async

जवाबों:


4

यह Nexus 7 (Adreno GPU) द्वारा गैर-अनुरूप व्यवहार है। आप कहते हैं, "वर्दी का मतलब बेतरतीब ढंग से पहुंचना नहीं है", लेकिन कल्पना के परिशिष्ट ए के अनुसार :

वर्दी (नमूने को छोड़कर)

शीर्ष shader में, सरणी अनुक्रमण के सभी रूपों के लिए समर्थन अनिवार्य है। टुकड़ा shader में, अनुक्रमणिका के लिए समर्थन केवल स्थिर-सूचकांक-अभिव्यक्तियों के लिए अनिवार्य है।

यहां चर्चा से लगता है कि यह बग केवल एकसमान मैट्रिक्स सरणियों पर लागू होता है, इसलिए वैक्टर का उपयोग करने वाले कार्य-के आसपास मज़बूती से काम करने और अन्य जीपीयू के लिए पोर्टेबल होने की संभावना है (मुझे पता है कि यादृच्छिक वर्दी अनुक्रमण कम से कम माली और पावर वीपीएन पर काम करता है)।


हम्म, मुझे लगता है कि सही लगता है। मुझे लगता है कि मैंने उस धागे को पहले पढ़ा था, लेकिन क्वालकॉम की ओर से इस समस्या की पुष्टि करने की कोई स्वीकृति नहीं है।
पांडा पजामा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.