ห้องคอมพิวเตอร์ 3 มิติที่เรนเดอร์ด้วย WebGL นั้นไม่มีอะไรใหม่แล้ว จุดประสงค์หลักของบทความนี้คือการอธิบายปัญหาของดวงตาและศูนย์กลางในห้องคอมพิวเตอร์ 3 มิติ ซึ่งบังเอิญถูกนำมาใช้ในโครงการนี้ หลังจากคิดมาได้สักระยะหนึ่งแล้วในที่สุดฉันก็ รู้สึกว่าตัวอย่างนี้ตรงกับความต้องการของฉันมากที่สุด ฉันจึงใช้เป็นบันทึก
การเรนเดอร์การสาธิตห้องคอมพิวเตอร์ 3 มิตินี้ค่อนข้างดี สวยงาม และการโต้ตอบพื้นฐานก็น่าพอใจ มาดูวิธีนำไปใช้กัน
การสร้างรหัส กำหนดชั้นเรียนขั้นแรก ให้เปิด js ที่เกี่ยวข้องทีละรายการจากเส้นทาง js ที่เรียกว่าใน index.html คลาส Editor.Server ได้รับการปรับแต่งใน server.js และถูกสร้างขึ้นโดยฟังก์ชัน ht.Default.def ที่ห่อหุ้มโดย HT (โปรดทราบว่าคลาสที่สร้างขึ้น ชื่อคือ Editor ตัวแก้ไขหน้า .Server ไม่สามารถแทนที่ด้วย E):
ht.Default.def('Editor.Server', Object, {//พารามิเตอร์ตัวแรกคือชื่อคลาส หากเป็นสตริง จะถูกลงทะเบียนโดยอัตโนมัติใน classMap ของ HT พารามิเตอร์ตัวที่สองคือคลาสพาเรนต์ที่จะสืบทอด โดยคลาสนี้ ; พารามิเตอร์ที่สามคือการประกาศวิธีการและตัวแปร addToDataModel: function(dm) { //เพิ่มโหนดลงในที่เก็บข้อมูล dm.add(this._node); // ฟังก์ชันที่กำหนดไว้ล่วงหน้าใน ht, ส่งผ่าน โหนดผ่านการเพิ่ม วิธีการเพิ่มไปยังที่เก็บข้อมูล}, setHost: function() { //ตั้งค่าการดูดซับ this._node.setHost.apply(this._node, arguments); }, s3: function() {//ตั้งค่าขนาดของโหนด this._node .s3.apply (this._node, อาร์กิวเมนต์); }, setElevation: ฟังก์ชั่น () {//ควบคุมตำแหน่งแกน y ของระบบพิกัด 3 มิติโดยที่ตำแหน่งกึ่งกลางของโหนดดั้งเดิมตั้งอยู่ this._node.setElevation.apply(this._node, arguments }});
สร้างคลาส Editor.Server
คลาสนี้สามารถสร้างโหนด ht.Node และตั้งค่าสีของโหนดและพื้นผิวด้านหน้า:
var S = E.Server = function(obj) {//ส่วนประกอบเซิร์ฟเวอร์ var color = obj.color, frontImg = obj.frontImg; var node = this._node = new ht.Node();//Create node node.s ({//ตั้งค่าสไตล์ของโหนดเป็นตัวย่อของ setStyle 'all.color': color,//ตั้งค่าสีของทั้งหกด้านของโหนด 'front.image': frontImg //ตั้งค่ารูปภาพไว้ที่ด้านหน้าของโหนด});};
ด้วยวิธีนี้ ฉันสามารถสร้างออบเจ็กต์ส่วนประกอบเซิร์ฟเวอร์ใหม่ได้โดยตรงซึ่งฉันต้องสร้างส่วนประกอบเซิร์ฟเวอร์ และฉันสามารถเรียกใช้ setHost และฟังก์ชันอื่นๆ ที่เราประกาศไว้ข้างต้นได้โดยตรง ซึ่งเราจะใช้ในเร็วๆ นี้
ถัดไป สร้างคลาส Cabinet Editor.Cabinet วิธีการจะคล้ายกับวิธีการนิยามของคลาส Editor.Server ด้านบน:
ht.Default.def('Editor.Cabinet', Object, { addToDataModel: function(dm) { dm.add(this._door); dm.add(this._node); this._serverList.forEach(ฟังก์ชั่น) { s.addToDataModel(dm } }, p3: ฟังก์ชั่น() { this._node.p3.apply(this._node, arguments);//ตั้งค่าพิกัด 3 มิติของโหนด}});
สร้างคลาส Editor.Cabinet
คลาสนี้ค่อนข้างซับซ้อนกว่าคลาสส่วนประกอบเซิร์ฟเวอร์ Editor.Server คลาสนี้สร้างตัวตู้ ประตูตู้ และส่วนประกอบเซิร์ฟเวอร์ภายในตู้:
var C = E.Cabinet = function(obj) { var color = obj.color, doorFrontImg = obj.doorFrontImg, doorBackImg = obj.doorBackImg, s3 = obj.s3; var node = this._node = ใหม่ ht.Node() ; // Cabinet node.s3(s3); // กำหนดขนาดของโหนดเป็น setSize3d node.a('cabinet', this);//ปรับแต่งคุณสมบัติของ Cabinet node.s({//ตั้งค่าสไตล์ของโหนดเป็น setStyle 'all.color': color,//กำหนดสีของทั้งหกด้านของโหนด 'front.visible ': false//ตั้งค่าว่าจะมองเห็นด้านหน้าของโหนดหรือไม่}); if (Math.random() > 0.5) { node.addStyleIcon('alarm', {//เพิ่มชื่อไอคอนให้กับโหนด: ['icon เทอร์โมมิเตอร์'], //อาร์เรย์ที่ประกอบด้วยหลายสตริง แต่ละสตริงสอดคล้องกับรูปภาพหรือเวกเตอร์ (ลงทะเบียนผ่าน ht.Default.setImage) ใบหน้า: 'top', //ค่าเริ่มต้นคือด้านหน้า ไอคอน การวางแนวในแบบ 3 มิติ ค่าที่มีอยู่คือ ซ้าย|ขวา|บน|ล่าง|หน้า|หลัง|ตำแหน่งกึ่งกลาง: 17, //ระบุตำแหน่งของไอคอน หมุนอัตโนมัติ: 'y', //ค่าเริ่มต้นคือเท็จ ไม่ว่าไอคอนจะหันหน้าไปทางดวงตาโดยอัตโนมัติในรูปแบบ 3D t3: [0, 16, 0], //ค่าเริ่มต้นคือไม่ได้กำหนดไว้, ออฟเซ็ตของไอคอนในรูปแบบ 3D, รูปแบบคือ [x ,y,z] ความกว้าง: 37 //ระบุความกว้างของแต่ละไอคอน ค่าเริ่มต้นจะขึ้นอยู่กับความกว้างเมื่อบันทึกความสูงของภาพ: 32 //ระบุความสูงของแต่ละไอคอน ค่าเริ่มต้นคือ ขึ้นอยู่กับความสูงเมื่อลงทะเบียนพื้นผิวภาพขนาด: 4, //ค่าเริ่มต้นคือ 2 ค่านี้แสดงถึงผลคูณของแผนที่จริงที่สร้างโดยหน่วยความจำ ไม่ควรตั้งค่าใหญ่เกินไป มิฉะนั้นจะส่งผลต่อประสิทธิภาพที่มองเห็นได้: { func: function() { return !! E.alarmVisible; }}//ระบุกลุ่มรูปภาพ }); } var door = this._door = new ht.DoorWindow();//Cabinet door door.setWidth(s3[0]);//ตั้งค่าความยาวขององค์ประกอบกราฟิกในทิศทางแกน x ในโทโพโลยี 3D door.setHeight(1);//ตั้งค่า องค์ประกอบกราฟิกในโทโพโลยี 3 มิติในแกน z ความยาว door.setTall(s3[1]);//ควบคุมความยาวของ Node primitive บนแกน y door.setElevation(0);//ตั้งค่าพิกัด y ของศูนย์กลางของ primitive ในประตูระบบพิกัด 3D .setY(s3[2 ] * 0.5);//ตั้งค่าตำแหน่งของโหนดบน door.setHost(node);//ตั้งค่าประตูการดูดซับ ({//ตั้งค่ารูปแบบโหนด setStyle 'all.color': color,/ /ตั้งค่าสีของโหนดทั้งหกด้าน 'front.image': doorFrontImg, // ตั้งค่ารูปภาพด้านหน้าของโหนด 'front.transparent': จริง, // ตั้งค่าว่าด้านหน้าของโหนดมีความโปร่งใส 'ด้านหลังหรือไม่ .image': doorBackImg, // ตั้งค่ารูปภาพที่ด้านหลังของโหนด 'back.uv': [1,0, 1,1, 0,1, 0,0], // ปรับแต่งแผนที่ uv ด้านหลังโหนด หากว่างเปล่า ค่าเริ่มต้น [0,0, 0,1, 1,1, 1 ,0] ' dw.axis': 'right'//ตั้งค่าแกนหมุนสำหรับการขยายองค์ประกอบ DoorWindow และการดำเนินการปิด ค่าที่เป็นไปได้คือ left|right|top|bottom|v|h }); var serverList = this ._serverList = []; max = 6, list = E.randomList(max, Math.floor(Math.random() * (max - 2)) + 2); //ฟังก์ชันเพื่อรับตัวเลขสุ่มที่ประกาศในเซิร์ฟเวอร์ global.js, h = s3 [0] / 4; list.forEach(function(r) { var server = new E.Server({ //สีส่วนประกอบของเซิร์ฟเวอร์: 'rgb(51,49,49)', frontImg: 'ส่วนประกอบเซิร์ฟเวอร์ดี' }); server.s3(s3[0] - 2, h, s3[2] - 4);//ตั้งค่าขนาดโหนด server.setElevation((r - max * 0.5) * (h + 2 ));//ตั้งค่าพิกัดของจุดศูนย์กลางโหนดบนแกน y server.setHost(node);//ตั้งค่าการดูดซับของโหนด serverList.push(server);//ถึง serverList เพิ่มโหนดเซิร์ฟเวอร์ใน });};
สิ่งเดียวที่ไม่ได้กล่าวถึงในโค้ดข้างต้นคือฟังก์ชัน Editor.randomList ฟังก์ชันนี้ได้รับการประกาศในไฟล์ global.js และมีการประกาศดังต่อไปนี้:
var E = window.Editor = { leftWidth: 0, topHeight: 40, RandomList: function(max, size) { var list = [], วิ่ง; while (list.length < size) { ran = Math.floor (Math. สุ่ม () * สูงสุด); if (list.indexOf(ran) >= 0) ดำเนินการต่อ; list.push(ran); } รายการส่งคืน;
โอเค ตอนนี้เมื่อคลาสสำหรับแต่ละส่วนของฉากถูกสร้างขึ้นแล้ว เราควรสร้างฉากแล้วรวมพื้นฐานเหล่านี้ลงไป!
การสร้างฉากนักเรียนที่คุ้นเคยควรรู้ว่าการใช้ HT เพื่อสร้างฉาก 3D ต้องใช้ส่วนประกอบ 3D ใหม่เท่านั้น จากนั้นจึงเพิ่มฉากลงในเนื้อหาผ่านฟังก์ชัน addToDOM:
var g3d = E.main = ใหม่ ht.graph3d.Graph3dView(); // ฉาก 3 มิติ
ไฟล์ main.js จะทำองค์ประกอบที่จำเป็นบางอย่างในฉาก 3 มิติเป็นหลัก เช่น ผนัง พื้น ประตู เครื่องปรับอากาศ และตำแหน่งการสร้างและปล่อยของตู้ทั้งหมด รวมถึงส่วนโต้ตอบที่สำคัญมาก
ฉันจะไม่โพสต์รหัสสำหรับการสร้างผนัง พื้น ประตู เครื่องปรับอากาศและตู้ หากคุณสนใจ โปรดตรวจสอบรหัสด้วยตัวเอง ในที่นี้ เราจะพูดถึงการดับเบิลคลิกที่ตู้และวัตถุใดๆ ที่เกี่ยวข้องกับตู้เป็นหลัก ( ประตูตู้ อุปกรณ์เซิร์ฟเวอร์) เพื่อสร้าง 3D ศูนย์กลางสายตาของกล้องจะเคลื่อนไปยังตำแหน่งหนึ่งตรงหน้าตู้ที่ดับเบิ้ลคลิก และการเคลื่อนไหวนี้ราบรื่นมากเมื่อก่อนฉันก็เลยคิดว่า เกี่ยวกับส่วนนี้มาเป็นเวลานาน และในที่สุดก็กล่าวถึงวิธีการใช้งานของการสาธิตนี้
เพื่อให้สามารถตั้งค่า eye และ center ซ้ำได้ เนื้อหาที่สอดคล้องกับการตั้งค่าพารามิเตอร์ทั้งสองนี้จึงถูกห่อหุ้มไว้ในเมธอด setEye และ setCenter วิธี setCenter จะคล้ายกับวิธี setEye และจะไม่ถูกทำซ้ำที่นี่:
//กำหนดตำแหน่งตา var setEye = function(eye, finish) { if (!eye) return; var e = g3d.getEye().slice(0),//รับค่า eye ปัจจุบัน dx = eye[0] - e[0], dy = eye[1] - e[1], dz = eye[2] - e[2]; // เริ่มการเปลี่ยนภาพเคลื่อนไหว 500 มิลลิวินาที ht.Default.startAnim({ ระยะเวลา: 500, การค่อยๆ เปลี่ยน: การค่อยๆ เปลี่ยน//ฟังก์ชันการค่อยๆ เปลี่ยนภาพเคลื่อนไหว finishFunc: เสร็จสิ้น || ฟังก์ชั่น() {}, // ฟังก์ชันที่ถูกเรียกหลังจากภาพเคลื่อนไหวสิ้นสุดลง การกระทำ: function(v, t) {//ตั้งค่าภาพเคลื่อนไหว v เพื่อแสดงถึงการค่อยๆ เปลี่ยน (t) ค่าหลังการทำงานของฟังก์ชัน t แสดงถึงความคืบหน้าของภาพเคลื่อนไหวปัจจุบัน [0~1] การเปลี่ยนแปลงคุณลักษณะทั่วไปจะขึ้นอยู่กับพารามิเตอร์ v g3d.setEye([ //ตั้งค่าตาในฉาก 3 มิติ ค่าสายตาเป็นอาร์เรย์ซึ่งสอดคล้องกับค่าของแกน x, y และ z ตามลำดับ e[0] + dx * v, e[1] + dy * v, e[2] + dz * โวลต์ ]);
ความจริงที่ว่าฉันไม่ได้ประกาศฟังก์ชัน setCenter ซ้ำ ๆ ไม่ได้หมายความว่าฟังก์ชันนี้ไม่สำคัญ ในทางกลับกัน ฟังก์ชันนี้มีบทบาทชี้ขาดในกระบวนการย้ายแนวสายตา ฟังก์ชัน setEye ข้างต้นเทียบเท่ากับที่ฉันต้องการ เพื่อเดินนำหน้าตำแหน่งเป้าหมายของฉัน (อย่างน้อยฉันก็กำหนดไว้ว่าใช้เพื่อจุดประสงค์นี้) ในขณะที่ sCenter คำจำกัดความคือการเลื่อนสายตาไปยังตำแหน่งของเป้าหมาย (เช่น ฉันสามารถยืนที่ตำแหน่งปัจจุบันของฉันและมองวัตถุที่อยู่ด้านหลังฉันไปทางขวาหรือฉันสามารถไปทางด้านหลังไปทางขวาและยืนอยู่ข้างหน้า วัตถุที่จะมอง) เป็นสิ่งสำคัญมากโปรดลิ้มรสมัน
เหตุการณ์ดับเบิลคลิกนั้นง่ายดาย เพียงฟังเหตุการณ์ที่สรุปโดย HT กำหนดประเภทเหตุการณ์ และดำเนินการที่เกี่ยวข้อง:
g3d.mi(function(e) {//addInteractorListener ฟังก์ชั่นการฟังเหตุการณ์ if (e.kind !== 'doubleClickData') // กำหนดประเภทเหตุการณ์ให้เป็นการส่งคืนโหนดแบบดับเบิลคลิก; var data = e.data, p3 ; if (data.a('cabinet')) //Body p3 = data.p3(); else { host = data.getHost(); // รับวัตถุการดูดซับของโหนดที่ถูกคลิก if (host && host.a('cabinet')) {//หากวัตถุการดูดซับคือ Cabinet p3 = host.p3(); } } ถ้า (!p3) กลับ; //ตั้งค่าตำแหน่งการเคลื่อนที่ของเป้าหมายตรงกลางเป็น ตำแหน่งของตู้ setEye([p3[0], 211, p3[2] + 247]); //กำหนดตำแหน่งที่ดวงตาจะเคลื่อนที่});แถบนำทางด้านบน
เมื่อฉันเห็นตัวอย่างนี้ครั้งแรก ฉันคิดว่าบุคคลนี้ยอดเยี่ยมมาก ฉันใช้ HT มานานแล้ว แต่ฉันยังไม่สามารถสร้างเอฟเฟกต์ที่สวยงามเช่นนี้โดยใช้ ht.widget.Toolbar ของ HT ได้ ตอนนั้นฉันก็รู้ว่านี่คือการใช้ HT จริงๆ มันสร้างมาด้วยรูปแบบ มันน่าทึ่งมาก ฉันโง่มาก
แบบฟอร์ม var = E.top = ใหม่ ht.widget.FormPane(); //ส่วนประกอบของแบบฟอร์มด้านบน form.setRowHeight(E.topHeight);//ตั้งค่าความสูงของแถว form.setVGap(-E.topHeight);//ตั้งค่าระยะห่างแนวนอนของส่วนประกอบของแบบฟอร์มให้เป็นค่าลบของความสูงของแถวที่จะคงไว้ หลายแถวในตำแหน่งเดียวกัน บรรทัด form.setVPadding(0);//ตั้งค่าด้านบนของแบบฟอร์มและระยะห่างระหว่างด้านบนและเนื้อหาส่วนประกอบ form.addRow([null, {//เพิ่มแถวของส่วนประกอบลงในแบบฟอร์ม พารามิเตอร์แรกคืออาร์เรย์ขององค์ประกอบ อาจเป็นสตริง ข้อมูลพารามิเตอร์ส่วนประกอบที่อธิบายในรูปแบบ json องค์ประกอบ html หรือรูปภาพ null: { icon: './สัญลักษณ์/ inputBG.json ', ยืด: 'centerUniform' }}], [40, 260]);//พารามิเตอร์ตัวที่สองคืออาร์เรย์ของข้อมูลความกว้างสำหรับแต่ละองค์ประกอบ ค่าความกว้างที่มากกว่า 1 แสดงถึงค่าสัมบูรณ์คงที่ และค่าความกว้างที่น้อยกว่าหรือเท่ากับ 1 แสดงถึงค่าสัมพัทธ์เช่นกัน การรวมกันของ 80+0.3 form.addRow([null, null , { id: 'searchInput', textField: {}}, { องค์ประกอบ: 'ระบบการจัดการภาพห้องคอมพิวเตอร์', สี: 'สีขาว', แบบอักษร: '18px arial , sans-serif'}, null, { ปุ่ม: { // ป้ายกำกับ: 'ViewChange', ไอคอน: './สัญลักษณ์/viewChange.json', พื้นหลัง: null, เลือกพื้นหลัง: 'rgb (128,128,128)', borderColor: 'rgba (0, 0, 0, 0 ) ', onClicked: function() { E.focusTo(); } }}, null, { ปุ่ม: { // ป้ายกำกับ: 'แจ้งเตือน', ไอคอน: './สัญลักษณ์/alarm.json', สลับได้: จริง, เลือกแล้ว: เท็จ, พื้นหลัง: null, เลือกพื้นหลัง: 'rgb (128,128,128)', borderColor: 'rgba (0, 0, 0, 0)', onClicked: ฟังก์ชั่น ( จ) { E.setAlarmVisible(this.isSelected()); } }}, null], [40, 42, 218, 300, 0.1, 50, 10, 50, 10]);
สิ่งที่กล่าวมาข้างต้นเป็นไปได้เท่านั้น แต่จริงๆ แล้วไม่ได้เพิ่มลงในแท็ก html ซึ่งหมายความว่าตอนนี้ไม่มีอะไรอยู่บนอินเทอร์เฟซแล้ว! อย่าลืมเพิ่มฉาก 3 มิติลงในเนื้อหาเมื่อโหลดเพจ และอย่าลืมเพิ่มแบบฟอร์มลงในเนื้อหาเมื่อตั้งค่าเหตุการณ์การเปลี่ยนแปลงขนาดหน้าต่าง แบบฟอร์มจะต้องได้รับการอัปเดตแบบเรียลไทม์ด้วย:
window.addEventListener('load', function() { g3d.addToDOM(); //เพิ่มฉาก 3 มิติลงในเนื้อหา document.body.appendChild(E.top.getView()); //เพิ่ม div พื้นฐานของ ส่วนประกอบของแบบฟอร์ม เพิ่มไปยังเนื้อหา window.addEventListener('resize', function() {//Listen สำหรับเหตุการณ์การเปลี่ยนแปลงขนาดหน้าต่าง E.top.iv();//อัปเดต div พื้นฐานของแบบฟอร์มแบบฟอร์ม -
นี่คือคำอธิบายของฟังก์ชัน addToDOM ซึ่งมีความสำคัญมากในการทำความเข้าใจกลไกของ HT โดยทั่วไปส่วนประกอบ HT จะถูกฝังอยู่ในคอนเทนเนอร์ เช่น BorderPane, SplitView และ TabView ส่วนประกอบ HT ภายนอกสุดต้องการให้ผู้ใช้เพิ่มองค์ประกอบ div พื้นฐานที่ส่งคืนโดย getView() ด้วยตนเองไปยังองค์ประกอบ DOM ของเพจ เมื่อขนาดของคอนเทนเนอร์หลักเปลี่ยนแปลง หากคอนเทนเนอร์หลักเป็นส่วนประกอบคอนเทนเนอร์ที่กำหนดไว้ล่วงหน้าของ HT เช่น BorderPane และ SplitView คอนเทนเนอร์ HT จะเรียกใช้ฟังก์ชันที่ไม่ถูกต้องขององค์ประกอบย่อยซ้ำโดยอัตโนมัติเพื่อแจ้งเตือนการอัปเดต แต่ถ้าคอนเทนเนอร์พาเรนต์เป็นองค์ประกอบ html ดั้งเดิม คอมโพเนนต์ HT จะไม่ทราบว่าจำเป็นต้องอัปเดต ดังนั้น โดยทั่วไปคอมโพเนนต์ HT ภายนอกสุดจำเป็นต้องฟังเหตุการณ์การเปลี่ยนแปลงขนาดหน้าต่างของหน้าต่างและเรียกใช้ฟังก์ชันที่ไม่ถูกต้องของส่วนนอกสุด ส่วนประกอบที่จะอัปเดต
เพื่ออำนวยความสะดวกในการโหลดส่วนประกอบภายนอกสุดให้เต็มหน้าต่าง ส่วนประกอบทั้งหมดของ HT มีฟังก์ชัน addToDOM และตรรกะการใช้งานมีดังนี้ โดยที่ iv เป็นตัวย่อของ invalidate:
addToDOM = function(){ var self = this, view = self.getView(), style = view.style; document.body.appendChild(view); //เพิ่ม div พื้นฐานของฉากลงใน body style.left = ' 0';//HT กำหนดตำแหน่งของ div ของส่วนประกอบทั้งหมดให้เป็นค่าสัมบูรณ์ style.right = '0'; style.top = '0'; style.bottom = '0'; window.addEventListener('resize', function () { self.iv(); }, false); //ฟังเหตุการณ์สำหรับการเปลี่ยนแปลงขนาดหน้าต่างและแจ้งการเปลี่ยนแปลงส่วนประกอบและการอัปเดต}
ด้วยวิธีนี้โค้ดทั้งหมดจะจบลง คุณสามารถคลิกขวาเพื่อตรวจสอบได้ด้วยตัวเอง และสามารถรับไฟล์ json ที่เกี่ยวข้องได้จากเครือข่าย
ข้างต้นคือเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่ามันจะเป็นประโยชน์ต่อการศึกษาของทุกคน ฉันหวังว่าทุกคนจะสนับสนุน VeVb Wulin Network