diff --git a/docs/api/ar/loaders/managers/DefaultLoadingManager.html b/docs/api/ar/loaders/managers/DefaultLoadingManager.html new file mode 100644 index 00000000000000..7fa15480bc662f --- /dev/null +++ b/docs/api/ar/loaders/managers/DefaultLoadingManager.html @@ -0,0 +1,68 @@ + + +
+ +
+ نسخة عالمية من [page:LoadingManager LoadingManager]، يستخدمها
+ معظم المحملات عندما لم يتم تحديد مدير مخصص.
+
+ هذا سيكون كافيًا لمعظم الأغراض، ولكن قد يكون هناك أوقات عندما
+ ترغب في مديري تحميل منفصلين للقول، القوام والنماذج.
+
+ يمكنك تعيين [page:LoadingManager.onStart onStart]،
+ [page:LoadingManager.onLoad onLoad]، [page:LoadingManager.onProgress onProgress]،
+ [page:LoadingManager.onStart onError] وظائف لل
+ مدير. ستطبق هذه على أي محملات تستخدم
+ DefaultLoadingManager.
+
+ يجب عدم الخلط بين هذه الوظائف المسماة بشكل مشابه
+ من المحملات الفردية، لأنها مخصصة لعرض المعلومات
+ حول الحالة العامة للتحميل، بدلاً من التعامل مع البيانات
+ التي تم تحميلها.
+
+THREE.DefaultLoadingManager.onStart = function ( url, itemsLoaded, itemsTotal ) {
+ console.log( 'Started loading file: ' + url + '.\nLoaded ' + itemsLoaded + ' of ' + itemsTotal + ' files.' );
+};
+
+THREE.DefaultLoadingManager.onLoad = function ( ) {
+ console.log( 'Loading Complete!');
+};
+
+THREE.DefaultLoadingManager.onProgress = function ( url, itemsLoaded, itemsTotal ) {
+ console.log( 'Loading file: ' + url + '.\nLoaded ' + itemsLoaded + ' of ' + itemsTotal + ' files.' );
+};
+
+THREE.DefaultLoadingManager.onError = function ( url ) {
+ console.log( 'There was an error loading ' + url );
+};
+
+
+ + انظر صفحة [page:LoadingManager LoadingManager] لتفاصيل + الخصائص. +
+ ++ انظر صفحة [page:LoadingManager LoadingManager] لتفاصيل الطرق. +
+ ++ [link:https://github.com/mrdoob/three.js/blob/master/src/loaders/LoadingManager.js src/loaders/LoadingManager.js] +
+ + diff --git a/docs/api/ar/loaders/managers/LoadingManager.html b/docs/api/ar/loaders/managers/LoadingManager.html new file mode 100644 index 00000000000000..bfe70192ed9d59 --- /dev/null +++ b/docs/api/ar/loaders/managers/LoadingManager.html @@ -0,0 +1,236 @@ + + + + +
+ يتعامل ويتتبع البيانات المحملة والمعلقة. يتم إنشاء نسخة عالمية افتراضية
+ من هذه الفئة واستخدامها من قبل المحملات إذا لم يتم توفيرها
+ يدويًا - انظر [page:DefaultLoadingManager].
+
+ بشكل عام يجب أن يكون ذلك كافيًا، ولكن هناك أوقات يمكن أن تكون فيها
+ مفيدًا لديك محملات منفصلة - على سبيل المثال إذا كنت ترغب في عرض
+ شرائط تحميل منفصلة للأشياء والقوام.
+
+ يوضح هذا المثال كيفية استخدام LoadingManager لتتبع التقدم + [page:OBJLoader]. +
+ +
+ const manager = new THREE.LoadingManager();
+ manager.onStart = function ( url, itemsLoaded, itemsTotal ) {
+ console.log( 'Started loading file: ' + url + '.\nLoaded ' + itemsLoaded + ' of ' + itemsTotal + ' files.' );
+ };
+
+ manager.onLoad = function ( ) {
+ console.log( 'Loading complete!');
+ };
+
+ manager.onProgress = function ( url, itemsLoaded, itemsTotal ) {
+ console.log( 'Loading file: ' + url + '.\nLoaded ' + itemsLoaded + ' of ' + itemsTotal + ' files.' );
+ };
+
+ manager.onError = function ( url ) {
+ console.log( 'There was an error loading ' + url );
+ };
+
+ const loader = new THREE.OBJLoader( manager );
+ loader.load( 'file.obj', function ( object ) {
+ //
+ } );
+
+
+ + بالإضافة إلى مراقبة التقدم، يمكن استخدام LoadingManager ل + تجاوز عناوين URL للموارد أثناء التحميل. قد يكون ذلك مفيدًا للأصول + القادمة من أحداث السحب والإفلات، WebSockets، WebRTC، أو غيرها من واجهات برمجة التطبيقات. A + مثال يظهر كيفية تحميل نموذج في الذاكرة باستخدام عناوين URL لـ Blob أدناه. +
+ +
+ // Blob or File objects created when dragging files into the webpage.
+ const blobs = {'fish.gltf': blob1, 'diffuse.png': blob2, 'normal.png': blob3};
+
+ const manager = new THREE.LoadingManager();
+
+ // Initialize loading manager with URL callback.
+ const objectURLs = [];
+ manager.setURLModifier( ( url ) => {
+
+ url = URL.createObjectURL( blobs[ url ] );
+ objectURLs.push( url );
+ return url;
+
+ } );
+
+ // Load as usual, then revoke the blob URLs.
+ const loader = new THREE.GLTFLoader( manager );
+ loader.load( 'fish.gltf', (gltf) => {
+
+ scene.add( gltf.scene );
+ objectURLs.forEach( ( url ) => URL.revokeObjectURL( url ) );
+
+ });
+
+
+
+ [example:webgl_loader_obj WebGL / loader / obj]
+ [example:webgl_postprocessing_outline WebGL / postprocesing / outline]
+
+ [page:Function onLoad] — (اختياري) سيتم استدعاء هذه الدالة عندما يتم
+ جميع المحملات.
+ [page:Function onProgress] — (اختياري) سيتم استدعاء هذه الدالة عند
+ اكتمال عنصر.
+ [page:Function onError] — (اختياري) سيتم استدعاء هذه الدالة عندما يواجه المحمل
+ أخطاء.
+
+ ينشئ جديدًا [name].
+
+ سيتم استدعاء هذه الدالة عند بدء التحميل. المعاملات هي:
+ [page:String url] — عنوان url للعنصر المحمل للتو.
+ [page:Integer itemsLoaded] — عدد العناصر المحملة حتى الآن.
+ [page:Integer itemsTotal] — إجمالي عدد العناصر التي يجب تحميلها.
+
+ بشكل افتراضي هذا غير محدد.
+
+ سيتم استدعاء هذه الدالة عند اكتمال جميع التحميلات. بشكل افتراضي + هذا غير محدد، ما لم يتم تمريره في المنشئ. +
+ +
+ سيتم استدعاء هذه الدالة عند اكتمال عنصر. المعاملات
+ هي:
+ [page:String url] — عنوان url للعنصر المحمل للتو.
+ [page:Integer itemsLoaded] — عدد العناصر المحملة حتى الآن.
+ [page:Integer itemsTotal] — إجمالي عدد العناصر التي يجب تحميلها.
+
+ بشكل افتراضي هذا غير محدد، ما لم يتم تمريره في المنشئ.
+
+ سيتم استدعاء هذه الدالة عند حدوث خطأ في أي عنصر، مع الحجة:
+ [page:String url] — عنوان url للعنصر الذي حدث فيه خطأ.
+
+ بشكل افتراضي هذا غير محدد، ما لم يتم تمريره في المنشئ.
+
+ [page:Object regex] — تعبير منتظم.
+ [page:Loader loader] — المحمل.
+
+ يسجل محملًا مع التعبير المنتظم المعطى. يمكن استخدامه ل + تحديد أي محمل يجب استخدامه لتحميل ملفات معينة. A + حالة استخدام نموذجية هي الكتابة فوق المحمل الافتراضي للقوام. +
+
+// add handler for TGA textures
+manager.addHandler( /\.tga$/i, new TGALoader() );
+
+
+ [page:String file] — مسار الملف.
+ ++ يمكن استخدامه لاسترجاع المحمل المسجل لمسار الملف المعطى. +
+ +[page:Object regex] — تعبير منتظم.
+ +يزيل المحمل للتعبير المنتظم المعطى.
+ +
+ [page:String url] — عنوان url للتحميل
+
+ بالنظر إلى عنوان URL، يستخدم رد الاتصال بتعديل URL (إن وجد) ويعيد
+ عنوان URL المحلول. إذا لم يتم تعيين تعديل URL، فسيتم إرجاع العنوان الأصلي.
+
+ [page:Function callback] — رد اتصال تعديل URL. يتم استدعاؤه مع [page:String url] الحجة،
+ ويجب أن يعود [page:String resolvedURL].
+
+ إذا تم توفيره، سيتم تمرير رد الاتصال لكل عنوان URL للمورد قبل
+ يتم إرسال طلب. قد يعود الرد الاتصال بالعنوان الأصلي، أو عنوان URL جديد ل
+ تجاوز سلوك التحميل. يمكن استخدام هذا السلوك لتحميل الأصول من
+ ملفات .ZIP، واجهات برمجة التطبيقات السحب والإفلات، وعناوين URI البيانات.
+
+ + ملاحظة: تم تصميم الطرق التالية ليتم استدعاؤها داخليًا من قبل + المحملات. يجب ألا تستدعيها مباشرة. + +
+ +
+ [page:String url] — عنوان url للتحميل
+
+ يجب استدعاء هذا من قبل أي محمل يستخدم المدير عندما يبدأ المحمل
+ تحميل عنوان url.
+
+ [page:String url] — عنوان url المحمَّل
+
+ يجب استدعاء هذا من قبل أي محمل يستخدم المدير عند انتهاء المحمل
+ تحميل عنوان url.
+
+ [page:String url] — عنوان url المحمَّل
+
+ يجب استدعاء هذا من قبل أي محمل يستخدم المدير عند حدوث خطأ في المحمل
+ تحميل عنوان url.
+
+ [link:https://github.com/mrdoob/three.js/blob/master/src/loaders/LoadingManager.js src/loaders/LoadingManager.js] +
+ + diff --git a/docs/api/en/math/Color.html b/docs/api/en/math/Color.html index 2e9f7def0d4e39..408ced10806d74 100644 --- a/docs/api/en/math/Color.html +++ b/docs/api/en/math/Color.html @@ -51,7 +51,7 @@Read-only flag to check if a given object is of type [name].
Red channel value between 0 and 1. Default is 1.
+Red channel value between `0` and `1`. Default is `1`.
Green channel value between 0 and 1. Default is 1.
+Green channel value between `0` and `1`. Default is `1`.
Blue channel value between 0 and 1. Default is 1.
+Blue channel value between `0` and `1`. Default is `1`.
[page:Object target] — the result will be copied into this Object. Adds h, @@ -218,8 +217,7 @@
[page:Color target] — the result will be copied into this object.
@@ -295,13 +293,15 @@
- [page:Color_Hex_or_String value] - Value to set this color to.
+ [page:Color_Hex_or_String r] - (optional) If arguments [page:Float g] and [page:Float b] are defined, the red component of the color. If they are
+ not defined, it can be a [link:https://en.wikipedia.org/wiki/Web_colors#Hex_triplet hexadecimal triplet] (recommended), a CSS-style string, or another `Color` instance.
+ [page:Float g] - (optional) If it is defined, the green component of the color.
+ [page:Float b] - (optional) If it is defined, the blue component of the color.
- See the Constructor above for full details of what
- [page:Color_Hex_or_String value] can be. Delegates to [page:.copy],
- [page:.setStyle], or [page:.setHex] depending on input type.
+ See the Constructor above for full details about possible arguments. Delegates to [page:.copy],
+ [page:.setStyle], [page:.setRGB] or [page:.setHex] depending on input type.
[page:Integer hex] — @@ -325,9 +324,9 @@
- [page:Float h] — hue value between 0.0 and 1.0
- [page:Float s] — saturation value between 0.0 and 1.0
- [page:Float l] — lightness value between 0.0 and 1.0
+ [page:Float h] — hue value between `0.0` and `1.0`
+ [page:Float s] — saturation value between `0.0` and `1.0`
+ [page:Float l] — lightness value between `0.0` and `1.0`
Sets color from HSL values.
- [page:Float r] — Red channel value between 0.0 and 1.0.
- [page:Float g] — Green channel value between 0.0 and 1.0.
- [page:Float b] — Blue channel value between 0.0 and 1.0.
+ [page:Float r] — Red channel value between `0.0` and `1.0`.
+ [page:Float g] — Green channel value between `0.0` and `1.0`.
+ [page:Float b] — Blue channel value between `0.0` and `1.0`.
Sets this color from RGB values.
- [page:Float scalar] — a value between 0.0 and 1.0.
+ [page:Float scalar] — a value between `0.0` and `1.0`.
Sets all three color components to the value [page:Float scalar].
[page:String style] — color as a CSS-style string.
diff --git a/docs/api/it/math/Color.html b/docs/api/it/math/Color.html
index ba7e7f0a7fc0fc..a3ae3341e5c5a3 100644
--- a/docs/api/it/math/Color.html
+++ b/docs/api/it/math/Color.html
@@ -181,7 +181,7 @@
[page:Color target] - questo risultato sarà copiato in questo oggetto.
diff --git a/docs/api/zh/math/Color.html b/docs/api/zh/math/Color.html
index b7d59f557f0e67..e335145a5d6983 100644
--- a/docs/api/zh/math/Color.html
+++ b/docs/api/zh/math/Color.html
@@ -182,7 +182,7 @@
[page:Color target] - 结果将复制到这个对象中.
diff --git a/docs/list.json b/docs/list.json
index 63a37b65f5e050..1b4d82772d474f 100644
--- a/docs/list.json
+++ b/docs/list.json
@@ -373,7 +373,6 @@
"OBJLoader": "examples/en/loaders/OBJLoader",
"PCDLoader": "examples/en/loaders/PCDLoader",
"PDBLoader": "examples/en/loaders/PDBLoader",
- "PRWMLoader": "examples/en/loaders/PRWMLoader",
"SVGLoader": "examples/en/loaders/SVGLoader",
"TGALoader": "examples/en/loaders/TGALoader"
},
@@ -495,7 +494,154 @@
"Cameras": {
"ArrayCamera": "api/ar/cameras/ArrayCamera",
"Camera": "api/ar/cameras/Camera",
- "CubeCamera": "api/ar/cameras/CubeCamera"
+ "CubeCamera": "api/ar/cameras/CubeCamera",
+ "OrthographicCamera": "api/ar/cameras/OrthographicCamera",
+ "PerspectiveCamera": "api/ar/cameras/PerspectiveCamera",
+ "StereoCamera": "api/ar/cameras/StereoCamera"
+ },
+
+ "Constants": {
+ "Animation": "api/ar/constants/Animation",
+ "Core": "api/ar/constants/Core",
+ "CustomBlendingEquation": "api/ar/constants/CustomBlendingEquations",
+ "BufferAttributeUsage": "api/ar/constants/BufferAttributeUsage",
+ "Materials": "api/ar/constants/Materials",
+ "Renderer": "api/ar/constants/Renderer",
+ "Textures": "api/ar/constants/Textures"
+ },
+
+ "Core": {
+ "BufferAttribute": "api/ar/core/BufferAttribute",
+ "BufferGeometry": "api/ar/core/BufferGeometry",
+ "Clock": "api/ar/core/Clock",
+ "EventDispatcher": "api/ar/core/EventDispatcher",
+ "GLBufferAttribute": "api/ar/core/GLBufferAttribute",
+ "InstancedBufferAttribute": "api/ar/core/InstancedBufferAttribute",
+ "InstancedBufferGeometry": "api/ar/core/InstancedBufferGeometry",
+ "InstancedInterleavedBuffer": "api/ar/core/InstancedInterleavedBuffer",
+ "InterleavedBuffer": "api/ar/core/InterleavedBuffer",
+ "InterleavedBufferAttribute": "api/ar/core/InterleavedBufferAttribute",
+ "Layers": "api/ar/core/Layers",
+ "Object3D": "api/ar/core/Object3D",
+ "Raycaster": "api/ar/core/Raycaster",
+ "Uniform": "api/ar/core/Uniform"
+ },
+
+ "Core / BufferAttributes": {
+ "BufferAttribute Types": "api/ar/core/bufferAttributeTypes/BufferAttributeTypes"
+ },
+
+ "Extras": {
+ "DataUtils": "api/ar/extras/DataUtils",
+ "Earcut": "api/ar/extras/Earcut",
+ "ImageUtils": "api/ar/extras/ImageUtils",
+ "PMREMGenerator": "api/ar/extras/PMREMGenerator",
+ "ShapeUtils": "api/ar/extras/ShapeUtils"
+ },
+
+ "Extras / Core": {
+ "Curve": "api/ar/extras/core/Curve",
+ "CurvePath": "api/ar/extras/core/CurvePath",
+ "Interpolations": "api/ar/extras/core/Interpolations",
+ "Path": "api/ar/extras/core/Path",
+ "Shape": "api/ar/extras/core/Shape",
+ "ShapePath": "api/ar/extras/core/ShapePath"
+ },
+
+ "Extras / Curves": {
+ "ArcCurve": "api/ar/extras/curves/ArcCurve",
+ "CatmullRomCurve3": "api/ar/extras/curves/CatmullRomCurve3",
+ "CubicBezierCurve": "api/ar/extras/curves/CubicBezierCurve",
+ "CubicBezierCurve3": "api/ar/extras/curves/CubicBezierCurve3",
+ "EllipseCurve": "api/ar/extras/curves/EllipseCurve",
+ "LineCurve": "api/ar/extras/curves/LineCurve",
+ "LineCurve3": "api/ar/extras/curves/LineCurve3",
+ "QuadraticBezierCurve": "api/ar/extras/curves/QuadraticBezierCurve",
+ "QuadraticBezierCurve3": "api/ar/extras/curves/QuadraticBezierCurve3",
+ "SplineCurve": "api/ar/extras/curves/SplineCurve"
+ },
+
+ "Geometries": {
+ "BoxGeometry": "api/ar/geometries/BoxGeometry",
+ "CapsuleGeometry": "api/ar/geometries/CapsuleGeometry",
+ "CircleGeometry": "api/ar/geometries/CircleGeometry",
+ "ConeGeometry": "api/ar/geometries/ConeGeometry",
+ "CylinderGeometry": "api/ar/geometries/CylinderGeometry",
+ "DodecahedronGeometry": "api/ar/geometries/DodecahedronGeometry",
+ "EdgesGeometry": "api/ar/geometries/EdgesGeometry",
+ "ExtrudeGeometry": "api/ar/geometries/ExtrudeGeometry",
+ "IcosahedronGeometry": "api/ar/geometries/IcosahedronGeometry",
+ "LatheGeometry": "api/ar/geometries/LatheGeometry",
+ "OctahedronGeometry": "api/ar/geometries/OctahedronGeometry",
+ "PlaneGeometry": "api/ar/geometries/PlaneGeometry",
+ "PolyhedronGeometry": "api/ar/geometries/PolyhedronGeometry",
+ "RingGeometry": "api/ar/geometries/RingGeometry",
+ "ShapeGeometry": "api/ar/geometries/ShapeGeometry",
+ "SphereGeometry": "api/ar/geometries/SphereGeometry",
+ "TetrahedronGeometry": "api/ar/geometries/TetrahedronGeometry",
+ "TorusGeometry": "api/ar/geometries/TorusGeometry",
+ "TorusKnotGeometry": "api/ar/geometries/TorusKnotGeometry",
+ "TubeGeometry": "api/ar/geometries/TubeGeometry",
+ "WireframeGeometry": "api/ar/geometries/WireframeGeometry"
+ },
+
+ "Helpers": {
+ "ArrowHelper": "api/ar/helpers/ArrowHelper",
+ "AxesHelper": "api/ar/helpers/AxesHelper",
+ "BoxHelper": "api/ar/helpers/BoxHelper",
+ "Box3Helper": "api/ar/helpers/Box3Helper",
+ "CameraHelper": "api/ar/helpers/CameraHelper",
+ "DirectionalLightHelper": "api/ar/helpers/DirectionalLightHelper",
+ "GridHelper": "api/ar/helpers/GridHelper",
+ "PolarGridHelper": "api/ar/helpers/PolarGridHelper",
+ "HemisphereLightHelper": "api/ar/helpers/HemisphereLightHelper",
+ "PlaneHelper": "api/ar/helpers/PlaneHelper",
+ "PointLightHelper": "api/ar/helpers/PointLightHelper",
+ "SkeletonHelper": "api/ar/helpers/SkeletonHelper",
+ "SpotLightHelper": "api/ar/helpers/SpotLightHelper"
+ },
+
+ "Lights": {
+ "AmbientLight": "api/ar/lights/AmbientLight",
+ "AmbientLightProbe": "api/ar/lights/AmbientLightProbe",
+ "DirectionalLight": "api/ar/lights/DirectionalLight",
+ "HemisphereLight": "api/ar/lights/HemisphereLight",
+ "HemisphereLightProbe": "api/ar/lights/HemisphereLightProbe",
+ "Light": "api/ar/lights/Light",
+ "LightProbe": "api/ar/lights/LightProbe",
+ "PointLight": "api/ar/lights/PointLight",
+ "RectAreaLight": "api/ar/lights/RectAreaLight",
+ "SpotLight": "api/ar/lights/SpotLight"
+ },
+
+ "Lights / Shadows": {
+ "LightShadow": "api/ar/lights/shadows/LightShadow",
+ "PointLightShadow": "api/ar/lights/shadows/PointLightShadow",
+ "DirectionalLightShadow": "api/ar/lights/shadows/DirectionalLightShadow",
+ "SpotLightShadow": "api/ar/lights/shadows/SpotLightShadow"
+ },
+
+ "Loaders": {
+ "AnimationLoader": "api/ar/loaders/AnimationLoader",
+ "AudioLoader": "api/ar/loaders/AudioLoader",
+ "BufferGeometryLoader": "api/ar/loaders/BufferGeometryLoader",
+ "Cache": "api/ar/loaders/Cache",
+ "CompressedTextureLoader": "api/ar/loaders/CompressedTextureLoader",
+ "CubeTextureLoader": "api/ar/loaders/CubeTextureLoader",
+ "DataTextureLoader": "api/ar/loaders/DataTextureLoader",
+ "FileLoader": "api/ar/loaders/FileLoader",
+ "ImageBitmapLoader": "api/ar/loaders/ImageBitmapLoader",
+ "ImageLoader": "api/ar/loaders/ImageLoader",
+ "Loader": "api/ar/loaders/Loader",
+ "LoaderUtils": "api/ar/loaders/LoaderUtils",
+ "MaterialLoader": "api/ar/loaders/MaterialLoader",
+ "ObjectLoader": "api/ar/loaders/ObjectLoader",
+ "TextureLoader": "api/ar/loaders/TextureLoader"
+ },
+
+ "Loaders / Managers": {
+ "DefaultLoadingManager": "api/ar/loaders/managers/DefaultLoadingManager",
+ "LoadingManager": "api/ar/loaders/managers/LoadingManager"
}
}
@@ -598,6 +744,7 @@
},
"附件": {
+ "DataUtils": "api/zh/extras/DataUtils",
"Earcut": "api/zh/extras/Earcut",
"ImageUtils": "api/zh/extras/ImageUtils",
"PMREMGenerator": "api/zh/extras/PMREMGenerator",
@@ -628,6 +775,7 @@
"几何体": {
"BoxGeometry": "api/zh/geometries/BoxGeometry",
+ "CapsuleGeometry": "api/zh/geometries/CapsuleGeometry",
"CircleGeometry": "api/zh/geometries/CircleGeometry",
"ConeGeometry": "api/zh/geometries/ConeGeometry",
"CylinderGeometry": "api/zh/geometries/CylinderGeometry",
@@ -776,6 +924,8 @@
},
"渲染器": {
+ "WebGL3DRenderTarget": "api/zh/renderers/WebGL3DRenderTarget",
+ "WebGLArrayRenderTarget": "api/zh/renderers/WebGLArrayRenderTarget",
"WebGLMultipleRenderTargets": "api/zh/renderers/WebGLMultipleRenderTargets",
"WebGLRenderer": "api/zh/renderers/WebGLRenderer",
"WebGL1Renderer": "api/zh/renderers/WebGL1Renderer",
@@ -849,11 +999,14 @@
"加载器": {
"FontLoader": "examples/zh/loaders/FontLoader",
+ "DracoLoader": "examples/zh/loaders/DracoLoader",
"GLTFLoader": "examples/zh/loaders/GLTFLoader",
"MMDLoader": "examples/zh/loaders/MMDLoader",
"MTLLoader": "examples/zh/loaders/MTLLoader",
"OBJLoader": "examples/zh/loaders/OBJLoader",
- "PCDLoader": "examples/zh/loaders/PCDLoader"
+ "PCDLoader": "examples/zh/loaders/PCDLoader",
+ "SVGLoader": "examples/zh/loaders/SVGLoader",
+ "TGALoader": "examples/zh/loaders/TGALoader"
},
"物体": {
@@ -1987,7 +2140,6 @@
"OBJLoader": "examples/en/loaders/OBJLoader",
"PCDLoader": "examples/en/loaders/PCDLoader",
"PDBLoader": "examples/en/loaders/PDBLoader",
- "PRWMLoader": "examples/en/loaders/PRWMLoader",
"SVGLoader": "examples/en/loaders/SVGLoader",
"TGALoader": "examples/en/loaders/TGALoader"
},
diff --git a/docs/page.js b/docs/page.js
index 92b7e407670573..d53df55f02ebb7 100644
--- a/docs/page.js
+++ b/docs/page.js
@@ -111,16 +111,32 @@ function onDocumentLoad() {
// handle code snippets formatting
+ function dedent( text ) {
+
+ // ignores singleline text
+ const lines = text.split( '\n' );
+ if ( lines.length <= 1 ) return text;
+
+ // ignores blank text
+ const nonBlankLine = lines.filter( l => l.trim() )[ 0 ];
+ if ( nonBlankLine === undefined ) return text;
+
+ // strips indents if any
+ const m = nonBlankLine.match( /^([\t ]+)/ );
+ if ( m ) text = lines.map( l => l.startsWith( m[ 1 ] ) ? l.substring( m[ 1 ].length ) : l ).join( '\n' );
+
+ // strips leading and trailing whitespaces finally
+ return text.trim();
+
+ }
+
const elements = document.getElementsByTagName( 'code' );
for ( let i = 0; i < elements.length; i ++ ) {
const element = elements[ i ];
- text = element.textContent.trim();
- text = text.replace( /^\t\t/gm, '' );
-
- element.textContent = text;
+ element.textContent = dedent( element.textContent );
}
diff --git a/examples/files.json b/examples/files.json
index d03a2f0225f8f4..b911079e9e8e30 100644
--- a/examples/files.json
+++ b/examples/files.json
@@ -328,6 +328,7 @@
"webgpu": [
"webgpu_audio_processing",
"webgpu_backdrop",
+ "webgpu_backdrop_area",
"webgpu_compute",
"webgpu_cubemap_adjustments",
"webgpu_cubemap_mix",
diff --git a/examples/jsm/nodes/Nodes.js b/examples/jsm/nodes/Nodes.js
index 2ba8263f8d114b..eddf15613e5768 100644
--- a/examples/jsm/nodes/Nodes.js
+++ b/examples/jsm/nodes/Nodes.js
@@ -63,16 +63,17 @@ export * from './shadernode/ShaderNode.js';
// accessors
export { default as BitangentNode, bitangentGeometry, bitangentLocal, bitangentView, bitangentWorld, transformedBitangentView, transformedBitangentWorld } from './accessors/BitangentNode.js';
export { default as BufferNode, buffer } from './accessors/BufferNode.js';
-export { default as CameraNode, cameraProjectionMatrix, cameraViewMatrix, cameraNormalMatrix, cameraWorldMatrix, cameraPosition } from './accessors/CameraNode.js';
+export { default as CameraNode, cameraProjectionMatrix, cameraViewMatrix, cameraNormalMatrix, cameraWorldMatrix, cameraPosition, cameraNear, cameraFar } from './accessors/CameraNode.js';
export { default as CubeTextureNode, cubeTexture } from './accessors/CubeTextureNode.js';
export { default as ExtendedMaterialNode, materialNormal } from './accessors/ExtendedMaterialNode.js';
export { default as InstanceNode, instance } from './accessors/InstanceNode.js';
export { default as MaterialNode, materialUV, materialAlphaTest, materialColor, materialShininess, materialEmissive, materialOpacity, materialSpecularColor, materialReflectivity, materialRoughness, materialMetalness, materialRotation } from './accessors/MaterialNode.js';
export { default as MaterialReferenceNode, materialReference } from './accessors/MaterialReferenceNode.js';
-export { default as ModelNode, modelDirection, modelViewMatrix, modelNormalMatrix, modelWorldMatrix, modelPosition, modelViewPosition } from './accessors/ModelNode.js';
+export { default as TextureBicubicNode, textureBicubic } from './accessors/TextureBicubicNode.js';
+export { default as ModelNode, modelDirection, modelViewMatrix, modelNormalMatrix, modelWorldMatrix, modelPosition, modelViewPosition, modelScale } from './accessors/ModelNode.js';
export { default as ModelViewProjectionNode, modelViewProjection } from './accessors/ModelViewProjectionNode.js';
export { default as NormalNode, normalGeometry, normalLocal, normalView, normalWorld, transformedNormalView, transformedNormalWorld } from './accessors/NormalNode.js';
-export { default as Object3DNode, objectDirection, objectViewMatrix, objectNormalMatrix, objectWorldMatrix, objectPosition, objectViewPosition } from './accessors/Object3DNode.js';
+export { default as Object3DNode, objectDirection, objectViewMatrix, objectNormalMatrix, objectWorldMatrix, objectPosition, objectScale, objectViewPosition } from './accessors/Object3DNode.js';
export { default as PointUVNode, pointUV } from './accessors/PointUVNode.js';
export { default as PositionNode, positionGeometry, positionLocal, positionWorld, positionWorldDirection, positionView, positionViewDirection } from './accessors/PositionNode.js';
export { default as ReferenceNode, reference } from './accessors/ReferenceNode.js';
@@ -80,7 +81,7 @@ export { default as ReflectVectorNode, reflectVector } from './accessors/Reflect
export { default as SkinningNode, skinning } from './accessors/SkinningNode.js';
export { default as StorageBufferNode, storage } from './accessors/StorageBufferNode.js';
export { default as TangentNode, tangentGeometry, tangentLocal, tangentView, tangentWorld, transformedTangentView, transformedTangentWorld } from './accessors/TangentNode.js';
-export { default as TextureNode, texture, sampler } from './accessors/TextureNode.js';
+export { default as TextureNode, texture, /*textureLevel,*/ sampler } from './accessors/TextureNode.js';
export { default as UVNode, uv } from './accessors/UVNode.js';
export { default as UserDataNode, userData } from './accessors/UserDataNode.js';
@@ -93,8 +94,10 @@ export { default as NormalMapNode, normalMap, TBNViewMatrix } from './display/No
export { default as PosterizeNode, posterize } from './display/PosterizeNode.js';
export { default as ToneMappingNode, toneMapping } from './display/ToneMappingNode.js';
export { default as ViewportNode, viewportCoordinate, viewportResolution, viewportTopLeft, viewportBottomLeft, viewportTopRight, viewportBottomRight } from './display/ViewportNode.js';
-export { default as ViewportTextureNode, viewportTexture } from './display/ViewportTextureNode.js';
+export { default as ViewportTextureNode, viewportTexture, viewportMipTexture } from './display/ViewportTextureNode.js';
export { default as ViewportSharedTextureNode, viewportSharedTexture } from './display/ViewportSharedTextureNode.js';
+export { default as ViewportDepthTextureNode, viewportDepthTexture } from './display/ViewportDepthTextureNode.js';
+export { default as ViewportDepthNode, viewZToOrthographicDepth, orthographicDepthToViewZ, viewZToPerspectiveDepth, perspectiveDepthToViewZ, depth, depthTexture } from './display/ViewportDepthNode.js';
// code
export { default as ExpressionNode, expression } from './code/ExpressionNode.js';
diff --git a/examples/jsm/nodes/accessors/CameraNode.js b/examples/jsm/nodes/accessors/CameraNode.js
index 7e41af5a01f62b..df37f6150be612 100644
--- a/examples/jsm/nodes/accessors/CameraNode.js
+++ b/examples/jsm/nodes/accessors/CameraNode.js
@@ -18,6 +18,10 @@ class CameraNode extends Object3DNode {
return 'mat4';
+ } else if ( scope === CameraNode.NEAR || scope === CameraNode.FAR ) {
+
+ return 'float';
+
}
return super.getNodeType( builder );
@@ -30,13 +34,21 @@ class CameraNode extends Object3DNode {
const uniformNode = this._uniformNode;
const scope = this.scope;
- if ( scope === CameraNode.PROJECTION_MATRIX ) {
+ if ( scope === CameraNode.VIEW_MATRIX ) {
+
+ uniformNode.value = camera.matrixWorldInverse;
+
+ } else if ( scope === CameraNode.PROJECTION_MATRIX ) {
uniformNode.value = camera.projectionMatrix;
- } else if ( scope === CameraNode.VIEW_MATRIX ) {
+ } else if ( scope === CameraNode.NEAR ) {
- uniformNode.value = camera.matrixWorldInverse;
+ uniformNode.value = camera.near;
+
+ } else if ( scope === CameraNode.FAR ) {
+
+ uniformNode.value = camera.far;
} else {
@@ -56,6 +68,10 @@ class CameraNode extends Object3DNode {
this._uniformNode.nodeType = 'mat4';
+ } else if ( scope === CameraNode.NEAR || scope === CameraNode.FAR ) {
+
+ this._uniformNode.nodeType = 'float';
+
}
return super.generate( builder );
@@ -65,10 +81,14 @@ class CameraNode extends Object3DNode {
}
CameraNode.PROJECTION_MATRIX = 'projectionMatrix';
+CameraNode.NEAR = 'near';
+CameraNode.FAR = 'far';
export default CameraNode;
export const cameraProjectionMatrix = nodeImmutable( CameraNode, CameraNode.PROJECTION_MATRIX );
+export const cameraNear = nodeImmutable( CameraNode, CameraNode.NEAR );
+export const cameraFar = nodeImmutable( CameraNode, CameraNode.FAR );
export const cameraViewMatrix = nodeImmutable( CameraNode, CameraNode.VIEW_MATRIX );
export const cameraNormalMatrix = nodeImmutable( CameraNode, CameraNode.NORMAL_MATRIX );
export const cameraWorldMatrix = nodeImmutable( CameraNode, CameraNode.WORLD_MATRIX );
diff --git a/examples/jsm/nodes/accessors/ModelNode.js b/examples/jsm/nodes/accessors/ModelNode.js
index d63adbd57b1242..831369a2fac17a 100644
--- a/examples/jsm/nodes/accessors/ModelNode.js
+++ b/examples/jsm/nodes/accessors/ModelNode.js
@@ -27,6 +27,7 @@ export const modelViewMatrix = nodeImmutable( ModelNode, ModelNode.VIEW_MATRIX )
export const modelNormalMatrix = nodeImmutable( ModelNode, ModelNode.NORMAL_MATRIX );
export const modelWorldMatrix = nodeImmutable( ModelNode, ModelNode.WORLD_MATRIX );
export const modelPosition = nodeImmutable( ModelNode, ModelNode.POSITION );
+export const modelScale = nodeImmutable( ModelNode, ModelNode.SCALE );
export const modelViewPosition = nodeImmutable( ModelNode, ModelNode.VIEW_POSITION );
addNodeClass( ModelNode );
diff --git a/examples/jsm/nodes/accessors/Object3DNode.js b/examples/jsm/nodes/accessors/Object3DNode.js
index 516fc32a8d82bb..9cfa4505b08664 100644
--- a/examples/jsm/nodes/accessors/Object3DNode.js
+++ b/examples/jsm/nodes/accessors/Object3DNode.js
@@ -32,7 +32,7 @@ class Object3DNode extends Node {
return 'mat3';
- } else if ( scope === Object3DNode.POSITION || scope === Object3DNode.VIEW_POSITION || scope === Object3DNode.DIRECTION ) {
+ } else if ( scope === Object3DNode.POSITION || scope === Object3DNode.VIEW_POSITION || scope === Object3DNode.DIRECTION || scope === Object3DNode.SCALE ) {
return 'vec3';
@@ -64,6 +64,12 @@ class Object3DNode extends Node {
uniformNode.value.setFromMatrixPosition( object.matrixWorld );
+ } else if ( scope === Object3DNode.SCALE ) {
+
+ uniformNode.value = uniformNode.value || new Vector3();
+
+ uniformNode.value.setFromMatrixScale( object.matrixWorld );
+
} else if ( scope === Object3DNode.DIRECTION ) {
uniformNode.value = uniformNode.value || new Vector3();
@@ -95,7 +101,7 @@ class Object3DNode extends Node {
this._uniformNode.nodeType = 'mat3';
- } else if ( scope === Object3DNode.POSITION || scope === Object3DNode.VIEW_POSITION || scope === Object3DNode.DIRECTION ) {
+ } else if ( scope === Object3DNode.POSITION || scope === Object3DNode.VIEW_POSITION || scope === Object3DNode.DIRECTION || scope === Object3DNode.SCALE ) {
this._uniformNode.nodeType = 'vec3';
@@ -127,6 +133,7 @@ Object3DNode.VIEW_MATRIX = 'viewMatrix';
Object3DNode.NORMAL_MATRIX = 'normalMatrix';
Object3DNode.WORLD_MATRIX = 'worldMatrix';
Object3DNode.POSITION = 'position';
+Object3DNode.SCALE = 'scale';
Object3DNode.VIEW_POSITION = 'viewPosition';
Object3DNode.DIRECTION = 'direction';
@@ -137,6 +144,7 @@ export const objectViewMatrix = nodeProxy( Object3DNode, Object3DNode.VIEW_MATRI
export const objectNormalMatrix = nodeProxy( Object3DNode, Object3DNode.NORMAL_MATRIX );
export const objectWorldMatrix = nodeProxy( Object3DNode, Object3DNode.WORLD_MATRIX );
export const objectPosition = nodeProxy( Object3DNode, Object3DNode.POSITION );
+export const objectScale = nodeProxy( Object3DNode, Object3DNode.SCALE );
export const objectViewPosition = nodeProxy( Object3DNode, Object3DNode.VIEW_POSITION );
addNodeClass( Object3DNode );
diff --git a/examples/jsm/nodes/accessors/TextureBicubicNode.js b/examples/jsm/nodes/accessors/TextureBicubicNode.js
new file mode 100644
index 00000000000000..846450d8f12aca
--- /dev/null
+++ b/examples/jsm/nodes/accessors/TextureBicubicNode.js
@@ -0,0 +1,94 @@
+import TempNode from '../core/TempNode.js';
+import { addNodeClass } from '../core/Node.js';
+import { add, mul, div } from '../math/OperatorNode.js';
+import { floor, ceil, fract, pow } from '../math/MathNode.js';
+import { nodeProxy, addNodeElement, float, vec2, vec4, int } from '../shadernode/ShaderNode.js';
+
+// Mipped Bicubic Texture Filtering by N8
+// https://www.shadertoy.com/view/Dl2SDW
+
+const bC = 1.0 / 6.0;
+
+const w0 = ( a ) => mul( bC, mul( a, mul( a, a.negate().add( 3.0 ) ).sub( 3.0 ) ).add( 1.0 ) );
+
+const w1 = ( a ) => mul( bC, mul( a, mul( a, mul( 3.0, a ).sub( 6.0 ) ) ).add( 4.0 ) );
+
+const w2 = ( a ) => mul( bC, mul( a, mul( a, mul( - 3.0, a ).add( 3.0 ) ).add( 3.0 ) ).add( 1.0 ) );
+
+const w3 = ( a ) => mul( bC, pow( a, 3 ) );
+
+const g0 = ( a ) => w0( a ).add( w1( a ) );
+
+const g1 = ( a ) => w2( a ).add( w3( a ) );
+
+// h0 and h1 are the two offset functions
+const h0 = ( a ) => add( - 1.0, w1( a ).div( w0( a ).add( w1( a ) ) ) );
+
+const h1 = ( a ) => add( 1.0, w3( a ).div( w2( a ).add( w3( a ) ) ) );
+
+const bicubic = ( textureNode, texelSize, lod ) => {
+
+ const uv = textureNode.uvNode;
+ const uvScaled = mul( uv, texelSize.zw ).add( 0.5 );
+
+ const iuv = floor( uvScaled );
+ const fuv = fract( uvScaled );
+
+ const g0x = g0( fuv.x );
+ const g1x = g1( fuv.x );
+ const h0x = h0( fuv.x );
+ const h1x = h1( fuv.x );
+ const h0y = h0( fuv.y );
+ const h1y = h1( fuv.y );
+
+ const p0 = vec2( iuv.x.add( h0x ), iuv.y.add( h0y ) ).sub( 0.5 ).mul( texelSize.xy );
+ const p1 = vec2( iuv.x.add( h1x ), iuv.y.add( h0y ) ).sub( 0.5 ).mul( texelSize.xy );
+ const p2 = vec2( iuv.x.add( h0x ), iuv.y.add( h1y ) ).sub( 0.5 ).mul( texelSize.xy );
+ const p3 = vec2( iuv.x.add( h1x ), iuv.y.add( h1y ) ).sub( 0.5 ).mul( texelSize.xy );
+
+ const a = g0( fuv.y ).mul( add( g0x.mul( textureNode.uv( p0 ).level( lod ) ), g1x.mul( textureNode.uv( p1 ).level( lod ) ) ) );
+ const b = g1( fuv.y ).mul( add( g0x.mul( textureNode.uv( p2 ).level( lod ) ), g1x.mul( textureNode.uv( p3 ).level( lod ) ) ) );
+
+ return a.add( b );
+
+};
+
+const textureBicubicMethod = ( textureNode, lodNode ) => {
+
+ const fLodSize = vec2( textureNode.size( int( lodNode ) ) );
+ const cLodSize = vec2( textureNode.size( int( lodNode.add( 1.0 ) ) ) );
+ const fLodSizeInv = div( 1.0, fLodSize );
+ const cLodSizeInv = div( 1.0, cLodSize );
+ const fSample = bicubic( textureNode, vec4( fLodSizeInv, fLodSize ), floor( lodNode ) );
+ const cSample = bicubic( textureNode, vec4( cLodSizeInv, cLodSize ), ceil( lodNode ) );
+
+ return fract( lodNode ).mix( fSample, cSample );
+
+};
+
+class TextureBicubicNode extends TempNode {
+
+ constructor( textureNode, blurNode = float( 3 ) ) {
+
+ super( 'vec4' );
+
+ this.textureNode = textureNode;
+ this.blurNode = blurNode;
+
+ }
+
+ construct() {
+
+ return textureBicubicMethod( this.textureNode, this.blurNode );
+
+ }
+
+}
+
+export default TextureBicubicNode;
+
+export const textureBicubic = nodeProxy( TextureBicubicNode );
+
+addNodeElement( 'bicubic', textureBicubic );
+
+addNodeClass( TextureBicubicNode );
diff --git a/examples/jsm/nodes/accessors/TextureNode.js b/examples/jsm/nodes/accessors/TextureNode.js
index fce9e2f6fcfcdb..a2c33d40ec1c1c 100644
--- a/examples/jsm/nodes/accessors/TextureNode.js
+++ b/examples/jsm/nodes/accessors/TextureNode.js
@@ -1,5 +1,7 @@
import UniformNode from '../core/UniformNode.js';
import { uv } from './UVNode.js';
+import { textureSize } from './TextureSizeNode.js';
+import { context } from '../core/ContextNode.js';
import { addNodeClass } from '../core/Node.js';
import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
@@ -140,6 +142,32 @@ class TextureNode extends UniformNode {
}
+ uv( uvNode ) {
+
+ const textureNode = this.clone();
+ textureNode.uvNode = uvNode;
+
+ return textureNode;
+
+ }
+
+ level( levelNode ) {
+
+ const textureNode = this.clone();
+ textureNode.levelNode = levelNode;
+
+ return context( textureNode, {
+ getMIPLevelAlgorithmNode: ( textureNode, levelNode ) => levelNode
+ } );
+
+ }
+
+ size( levelNode ) {
+
+ return textureSize( this, levelNode );
+
+ }
+
serialize( data ) {
super.serialize( data );
@@ -156,13 +184,22 @@ class TextureNode extends UniformNode {
}
+ clone() {
+
+ return new this.constructor( this.value, this.uvNode, this.levelNode );
+
+ }
+
}
export default TextureNode;
export const texture = nodeProxy( TextureNode );
+//export const textureLevel = ( value, uv, level ) => texture( value, uv ).level( level );
+
export const sampler = ( aTexture ) => ( aTexture.isNode === true ? aTexture : texture( aTexture ) ).convert( 'sampler' );
addNodeElement( 'texture', texture );
+//addNodeElement( 'textureLevel', textureLevel );
addNodeClass( TextureNode );
diff --git a/examples/jsm/nodes/accessors/TextureSizeNode.js b/examples/jsm/nodes/accessors/TextureSizeNode.js
new file mode 100644
index 00000000000000..04501f1a16fdd1
--- /dev/null
+++ b/examples/jsm/nodes/accessors/TextureSizeNode.js
@@ -0,0 +1,35 @@
+import Node from '../core/Node.js';
+import { addNodeClass } from '../core/Node.js';
+import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
+
+class TextureSizeNode extends Node {
+
+ constructor( textureNode, levelNode = null ) {
+
+ super( 'uvec2' );
+
+ this.isTextureSizeNode = true;
+
+ this.textureNode = textureNode;
+ this.levelNode = levelNode;
+
+ }
+
+ generate( builder, output ) {
+
+ const textureProperty = this.textureNode.build( builder, 'property' );
+ const levelNode = this.levelNode.build( builder, 'int' );
+
+ return builder.format( `textureDimensions( ${textureProperty}, ${levelNode} )`, this.getNodeType( builder ), output );
+
+ }
+
+}
+
+export default TextureSizeNode;
+
+export const textureSize = nodeProxy( TextureSizeNode );
+
+addNodeElement( 'textureSize', textureSize );
+
+addNodeClass( TextureSizeNode );
diff --git a/examples/jsm/nodes/display/ViewportDepthNode.js b/examples/jsm/nodes/display/ViewportDepthNode.js
new file mode 100644
index 00000000000000..7c31c7f4d6349e
--- /dev/null
+++ b/examples/jsm/nodes/display/ViewportDepthNode.js
@@ -0,0 +1,69 @@
+import Node, { addNodeClass } from '../core/Node.js';
+import { nodeImmutable, nodeProxy } from '../shadernode/ShaderNode.js';
+import { cameraNear, cameraFar } from '../accessors/CameraNode.js';
+import { positionView } from '../accessors/PositionNode.js';
+import { viewportDepthTexture } from './ViewportDepthTextureNode.js';
+
+class ViewportDepthNode extends Node {
+
+ constructor( scope, textureNode = null ) {
+
+ super( 'float' );
+
+ this.scope = scope;
+ this.textureNode = textureNode;
+
+ this.isViewportDepthNode = true;
+
+ }
+
+ construct( /*builder*/ ) {
+
+ const { scope } = this;
+
+ let node = null;
+
+ if ( scope === ViewportDepthNode.DEPTH ) {
+
+ node = viewZToOrthographicDepth( positionView.z, cameraNear, cameraFar );
+
+ } else if ( scope === ViewportDepthNode.DEPTH_TEXTURE ) {
+
+ const texture = this.textureNode || viewportDepthTexture();
+
+ const viewZ = perspectiveDepthToViewZ( texture, cameraNear, cameraFar );
+ node = viewZToOrthographicDepth( viewZ, cameraNear, cameraFar );
+
+ }
+
+ return node;
+
+ }
+
+}
+
+// NOTE: viewZ, the z-coordinate in camera space, is negative for points in front of the camera
+
+// -near maps to 0; -far maps to 1
+export const viewZToOrthographicDepth = ( viewZ, near, far ) => viewZ.add( near ).div( near.sub( far ) );
+
+// maps orthographic depth in [ 0, 1 ] to viewZ
+export const orthographicDepthToViewZ = ( depth, near, far ) => near.sub( far ).mul( depth ).sub( near );
+
+// NOTE: https://twitter.com/gonnavis/status/1377183786949959682
+
+// -near maps to 0; -far maps to 1
+export const viewZToPerspectiveDepth = ( viewZ, near, far ) => near.add( viewZ ).mul( far ).div( near.sub( far ).mul( viewZ ) );
+
+// maps perspective depth in [ 0, 1 ] to viewZ
+export const perspectiveDepthToViewZ = ( depth, near, far ) => near.mul( far ).div( far.sub( near ).mul( depth ).sub( far ) );
+
+ViewportDepthNode.DEPTH = 'depth';
+ViewportDepthNode.DEPTH_TEXTURE = 'depthTexture';
+
+export default ViewportDepthNode;
+
+export const depth = nodeImmutable( ViewportDepthNode, ViewportDepthNode.DEPTH );
+export const depthTexture = nodeProxy( ViewportDepthNode, ViewportDepthNode.DEPTH_TEXTURE );
+
+addNodeClass( ViewportDepthNode );
diff --git a/examples/jsm/nodes/display/ViewportDepthTextureNode.js b/examples/jsm/nodes/display/ViewportDepthTextureNode.js
new file mode 100644
index 00000000000000..d090ad2b020b64
--- /dev/null
+++ b/examples/jsm/nodes/display/ViewportDepthTextureNode.js
@@ -0,0 +1,34 @@
+import ViewportTextureNode from './ViewportTextureNode.js';
+import { addNodeClass } from '../core/Node.js';
+import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
+import { viewportTopLeft } from './ViewportNode.js';
+import { DepthTexture, LinearMipmapLinearFilter, DepthFormat, UnsignedIntType } from 'three';
+
+let sharedDepthbuffer = null;
+
+class ViewportDepthTextureNode extends ViewportTextureNode {
+
+ constructor( uvNode = viewportTopLeft, levelNode = null ) {
+
+ if ( sharedDepthbuffer === null ) {
+
+ sharedDepthbuffer = new DepthTexture();
+ sharedDepthbuffer.minFilter = LinearMipmapLinearFilter;
+ sharedDepthbuffer.type = UnsignedIntType;
+ sharedDepthbuffer.format = DepthFormat;
+
+ }
+
+ super( uvNode, levelNode, sharedDepthbuffer );
+
+ }
+
+}
+
+export default ViewportDepthTextureNode;
+
+export const viewportDepthTexture = nodeProxy( ViewportDepthTextureNode );
+
+addNodeElement( 'viewportDepthTexture', viewportDepthTexture );
+
+addNodeClass( ViewportDepthTextureNode );
diff --git a/examples/jsm/nodes/display/ViewportSharedTextureNode.js b/examples/jsm/nodes/display/ViewportSharedTextureNode.js
index 395a3daeb2a871..853f26feeda7e6 100644
--- a/examples/jsm/nodes/display/ViewportSharedTextureNode.js
+++ b/examples/jsm/nodes/display/ViewportSharedTextureNode.js
@@ -2,20 +2,21 @@ import ViewportTextureNode from './ViewportTextureNode.js';
import { addNodeClass } from '../core/Node.js';
import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
import { viewportTopLeft } from './ViewportNode.js';
+import { FramebufferTexture } from 'three';
-let rtt = null;
+let sharedFramebuffer = null;
class ViewportSharedTextureNode extends ViewportTextureNode {
- constructor( uv = viewportTopLeft ) {
+ constructor( uvNode = viewportTopLeft, levelNode = null ) {
- super( uv );
+ if ( sharedFramebuffer === null ) {
- }
+ sharedFramebuffer = new FramebufferTexture();
- constructRTT( builder ) {
+ }
- return rtt || ( rtt = builder.getRenderTarget() );
+ super( uvNode, levelNode, sharedFramebuffer );
}
diff --git a/examples/jsm/nodes/display/ViewportTextureNode.js b/examples/jsm/nodes/display/ViewportTextureNode.js
index 57a1d6308b287f..356f5c28b4681d 100644
--- a/examples/jsm/nodes/display/ViewportTextureNode.js
+++ b/examples/jsm/nodes/display/ViewportTextureNode.js
@@ -3,17 +3,24 @@ import { NodeUpdateType } from '../core/constants.js';
import { addNodeClass } from '../core/Node.js';
import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
import { viewportTopLeft } from './ViewportNode.js';
-import { Vector2 } from 'three';
+import { Vector2, FramebufferTexture, LinearMipmapLinearFilter } from 'three';
-let size = new Vector2();
+const _size = new Vector2();
class ViewportTextureNode extends TextureNode {
- constructor( uv = viewportTopLeft, level = null ) {
+ constructor( uvNode = viewportTopLeft, levelNode = null, framebufferTexture = null ) {
- super( null, uv, level );
+ if ( framebufferTexture === null ) {
- this.rtt = null;
+ framebufferTexture = new FramebufferTexture();
+ framebufferTexture.minFilter = LinearMipmapLinearFilter;
+
+ }
+
+ super( framebufferTexture, uvNode, levelNode );
+
+ this.generateMipmaps = false;
this.isOutputTextureNode = true;
@@ -21,32 +28,37 @@ class ViewportTextureNode extends TextureNode {
}
- constructRTT( builder ) {
+ updateBefore( frame ) {
- return builder.getRenderTarget();
+ const renderer = frame.renderer;
+ renderer.getDrawingBufferSize( _size );
- }
+ //
- construct( builder ) {
+ const framebufferTexture = this.value;
- if ( this.rtt === null ) this.rtt = this.constructRTT( builder );
+ if ( framebufferTexture.image.width !== _size.width || framebufferTexture.image.height !== _size.height ) {
- this.value = this.rtt.texture;
+ framebufferTexture.image.width = _size.width;
+ framebufferTexture.image.height = _size.height;
+ framebufferTexture.needsUpdate = true;
- return super.construct( builder );
+ }
- }
+ //
- updateBefore( frame ) {
+ const currentGenerateMipmaps = framebufferTexture.generateMipmaps;
+ framebufferTexture.generateMipmaps = this.generateMipmaps;
- const rtt = this.rtt;
+ renderer.copyFramebufferToTexture( framebufferTexture );
- const renderer = frame.renderer;
- renderer.getDrawingBufferSize( size );
+ framebufferTexture.generateMipmaps = currentGenerateMipmaps;
+
+ }
- rtt.setSize( size.width, size.height );
+ clone() {
- renderer.copyFramebufferToRenderTarget( rtt );
+ return new this.constructor( this.uvNode, this.levelNode, this.value );
}
@@ -55,7 +67,9 @@ class ViewportTextureNode extends TextureNode {
export default ViewportTextureNode;
export const viewportTexture = nodeProxy( ViewportTextureNode );
+export const viewportMipTexture = nodeProxy( ViewportTextureNode, null, null, { generateMipmaps: true } );
addNodeElement( 'viewportTexture', viewportTexture );
+addNodeElement( 'viewportMipTexture', viewportMipTexture );
addNodeClass( ViewportTextureNode );
diff --git a/examples/jsm/postprocessing/TAARenderPass.js b/examples/jsm/postprocessing/TAARenderPass.js
index 6a7180ae840de0..cbbea08b6601b2 100644
--- a/examples/jsm/postprocessing/TAARenderPass.js
+++ b/examples/jsm/postprocessing/TAARenderPass.js
@@ -1,4 +1,5 @@
import {
+ HalfFloatType,
WebGLRenderTarget
} from 'three';
import { SSAARenderPass } from './SSAARenderPass.js';
@@ -41,14 +42,14 @@ class TAARenderPass extends SSAARenderPass {
if ( this.sampleRenderTarget === undefined ) {
- this.sampleRenderTarget = new WebGLRenderTarget( readBuffer.width, readBuffer.height, this.params );
+ this.sampleRenderTarget = new WebGLRenderTarget( readBuffer.width, readBuffer.height, { type: HalfFloatType } );
this.sampleRenderTarget.texture.name = 'TAARenderPass.sample';
}
if ( this.holdRenderTarget === undefined ) {
- this.holdRenderTarget = new WebGLRenderTarget( readBuffer.width, readBuffer.height, this.params );
+ this.holdRenderTarget = new WebGLRenderTarget( readBuffer.width, readBuffer.height, { type: HalfFloatType } );
this.holdRenderTarget.texture.name = 'TAARenderPass.hold';
}
@@ -64,6 +65,9 @@ class TAARenderPass extends SSAARenderPass {
const autoClear = renderer.autoClear;
renderer.autoClear = false;
+ renderer.getClearColor( this._oldClearColor );
+ const oldClearAlpha = renderer.getClearAlpha();
+
const sampleWeight = 1.0 / ( jitterOffsets.length );
if ( this.accumulateIndex >= 0 && this.accumulateIndex < jitterOffsets.length ) {
@@ -87,11 +91,18 @@ class TAARenderPass extends SSAARenderPass {
}
renderer.setRenderTarget( writeBuffer );
+ renderer.setClearColor( this.clearColor, this.clearAlpha );
renderer.clear();
renderer.render( this.scene, this.camera );
renderer.setRenderTarget( this.sampleRenderTarget );
- if ( this.accumulateIndex === 0 ) renderer.clear();
+ if ( this.accumulateIndex === 0 ) {
+
+ renderer.setClearColor( 0x000000, 0.0 );
+ renderer.clear();
+
+ }
+
this.fsQuad.render( renderer );
this.accumulateIndex ++;
@@ -104,6 +115,7 @@ class TAARenderPass extends SSAARenderPass {
}
+ renderer.setClearColor( this.clearColor, this.clearAlpha );
const accumulationWeight = this.accumulateIndex * sampleWeight;
if ( accumulationWeight > 0 ) {
@@ -121,12 +133,12 @@ class TAARenderPass extends SSAARenderPass {
this.copyUniforms[ 'opacity' ].value = 1.0 - accumulationWeight;
this.copyUniforms[ 'tDiffuse' ].value = this.holdRenderTarget.texture;
renderer.setRenderTarget( writeBuffer );
- if ( accumulationWeight === 0 ) renderer.clear();
this.fsQuad.render( renderer );
}
renderer.autoClear = autoClear;
+ renderer.setClearColor( this._oldClearColor, oldClearAlpha );
}
diff --git a/examples/jsm/renderers/common/Renderer.js b/examples/jsm/renderers/common/Renderer.js
new file mode 100644
index 00000000000000..848b7f091b930b
--- /dev/null
+++ b/examples/jsm/renderers/common/Renderer.js
@@ -0,0 +1,828 @@
+import Animation from './Animation.js';
+import RenderObjects from './RenderObjects.js';
+import Attributes from './Attributes.js';
+import Geometries from './Geometries.js';
+import Info from './Info.js';
+import Pipelines from './Pipelines.js';
+import Bindings from './Bindings.js';
+import RenderLists from './RenderLists.js';
+import RenderContexts from './RenderContexts.js';
+import Textures from './Textures.js';
+import Background from './Background.js';
+import Nodes from './nodes/Nodes.js';
+import { Frustum, Matrix4, Vector2, Vector3, Vector4, Color, SRGBColorSpace, NoToneMapping } from 'three';
+
+const _drawingBufferSize = new Vector2();
+const _screen = new Vector4();
+const _frustum = new Frustum();
+const _projScreenMatrix = new Matrix4();
+const _vector3 = new Vector3();
+
+class Renderer {
+
+ constructor( backend ) {
+
+ this.isRenderer = true;
+
+ // public
+
+ this.domElement = backend.getDomElement();
+
+ this.backend = backend;
+
+ this.autoClear = true;
+ this.autoClearColor = true;
+ this.autoClearDepth = true;
+ this.autoClearStencil = true;
+
+ this.outputColorSpace = SRGBColorSpace;
+
+ this.toneMapping = NoToneMapping;
+ this.toneMappingExposure = 1.0;
+
+ this.sortObjects = true;
+
+ this.depth = true;
+ this.stencil = true;
+
+ // internals
+
+ this._pixelRatio = 1;
+ this._width = this.domElement.width;
+ this._height = this.domElement.height;
+
+ this._viewport = new Vector4( 0, 0, this._width, this._height );
+ this._scissor = new Vector4( 0, 0, this._width, this._height );
+ this._scissorTest = false;
+
+ this._info = null;
+ this._properties = null;
+ this._attributes = null;
+ this._geometries = null;
+ this._nodes = null;
+ this._bindings = null;
+ this._objects = null;
+ this._pipelines = null;
+ this._renderLists = null;
+ this._renderContexts = null;
+ this._textures = null;
+ this._background = null;
+
+ this._animation = new Animation();
+
+ this._currentRenderContext = null;
+ this._lastRenderContext = null;
+
+ this._opaqueSort = null;
+ this._transparentSort = null;
+
+ this._clearAlpha = 1;
+ this._clearColor = new Color( 0x000000 );
+ this._clearDepth = 1;
+ this._clearStencil = 0;
+
+ this._renderTarget = null;
+ this._currentActiveCubeFace = 0;
+
+ this._initialized = false;
+ this._initPromise = null;
+
+ // backwards compatibility
+
+ this.shadowMap = {
+ enabled: false,
+ type: null
+ };
+
+ this.xr = {
+ enabled: false
+ };
+
+ }
+
+ async init() {
+
+ if ( this._initialized ) {
+
+ throw new Error( 'Renderer: Backend has already been initialized.' );
+
+ }
+
+ if ( this._initPromise !== null ) {
+
+ return this._initPromise;
+
+ }
+
+ this._initPromise = new Promise( async ( resolve, reject ) => {
+
+ const backend = this.backend;
+
+ try {
+
+ await backend.init( this );
+
+ } catch ( error ) {
+
+ reject( error );
+ return;
+
+ }
+
+ this._info = new Info();
+ this._nodes = new Nodes( this, backend );
+ this._attributes = new Attributes( backend );
+ this._background = new Background( this, this._nodes );
+ this._geometries = new Geometries( this._attributes, this._info );
+ this._textures = new Textures( backend, this._info );
+ this._pipelines = new Pipelines( backend, this._nodes );
+ this._bindings = new Bindings( backend, this._nodes, this._textures, this._attributes, this._pipelines, this._info );
+ this._objects = new RenderObjects( this, this._nodes, this._geometries, this._pipelines, this._info );
+ this._renderLists = new RenderLists();
+ this._renderContexts = new RenderContexts();
+
+ //
+
+ this._animation.setNodes( this._nodes );
+ this._animation.start();
+
+ this._initialized = true;
+
+ resolve();
+
+ } );
+
+ return this._initPromise;
+
+ }
+
+ get coordinateSystem() {
+
+ return this.backend.coordinateSystem;
+
+ }
+
+ async compile( /*scene, camera*/ ) {
+
+ console.warn( 'THREE.Renderer: .compile() is not implemented yet.' );
+
+ }
+
+ async render( scene, camera ) {
+
+ if ( this._initialized === false ) await this.init();
+
+ // preserve render tree
+
+ const nodeFrame = this._nodes.nodeFrame;
+
+ const previousRenderId = nodeFrame.renderId;
+ const previousRenderState = this._currentRenderContext;
+
+ //
+
+ const renderContext = this._renderContexts.get( scene, camera );
+ const renderTarget = this._renderTarget;
+ const activeCubeFace = this._activeCubeFace;
+
+ this._currentRenderContext = renderContext;
+
+ nodeFrame.renderId ++;
+
+ //
+
+ const coordinateSystem = this.coordinateSystem;
+
+ if ( camera.coordinateSystem !== coordinateSystem ) {
+
+ camera.coordinateSystem = coordinateSystem;
+
+ camera.updateProjectionMatrix();
+
+ }
+
+ //
+
+ if ( this._animation.isAnimating === false ) nodeFrame.update();
+
+ if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
+
+ if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
+
+ if ( this._info.autoReset === true ) this._info.reset();
+
+ this._info.render.frame ++;
+
+ //
+
+ let viewport = this._viewport;
+ let scissor = this._scissor;
+ let pixelRatio = this._pixelRatio;
+
+ if ( renderTarget !== null ) {
+
+ viewport = renderTarget.viewport;
+ scissor = renderTarget.scissor;
+ pixelRatio = 1;
+
+ }
+
+ this.getDrawingBufferSize( _drawingBufferSize );
+
+ _screen.set( 0, 0, _drawingBufferSize.width, _drawingBufferSize.height );
+
+ const minDepth = ( viewport.minDepth === undefined ) ? 0 : viewport.minDepth;
+ const maxDepth = ( viewport.maxDepth === undefined ) ? 1 : viewport.maxDepth;
+
+ renderContext.viewportValue.copy( viewport ).multiplyScalar( pixelRatio ).floor();
+ renderContext.viewportValue.minDepth = minDepth;
+ renderContext.viewportValue.maxDepth = maxDepth;
+ renderContext.viewport = renderContext.viewportValue.equals( _screen ) === false;
+
+ renderContext.scissorValue.copy( scissor ).multiplyScalar( pixelRatio ).floor();
+ renderContext.scissor = this._scissorTest && renderContext.scissorValue.equals( _screen ) === false;
+
+ renderContext.depth = this.depth;
+ renderContext.stencil = this.stencil;
+
+ //
+
+ _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
+ _frustum.setFromProjectionMatrix( _projScreenMatrix, coordinateSystem );
+
+ const renderList = this._renderLists.get( scene, camera );
+ renderList.init();
+
+ this._projectObject( scene, camera, 0, renderList );
+
+ renderList.finish();
+
+ if ( this.sortObjects === true ) {
+
+ renderList.sort( this._opaqueSort, this._transparentSort );
+
+ }
+
+ //
+
+ if ( renderTarget !== null ) {
+
+ this._textures.updateRenderTarget( renderTarget );
+
+ const renderTargetData = this._textures.get( renderTarget );
+
+ renderContext.texture = renderTargetData.texture;
+ renderContext.depthTexture = renderTargetData.depthTexture;
+
+ } else {
+
+ renderContext.texture = null;
+ renderContext.depthTexture = null;
+
+ }
+
+ renderContext.activeCubeFace = activeCubeFace;
+
+ //
+
+ this._nodes.updateScene( scene );
+
+ //
+
+ this._background.update( scene, renderList, renderContext );
+
+ //
+
+ this.backend.beginRender( renderContext );
+
+ // process render lists
+
+ const opaqueObjects = renderList.opaque;
+ const transparentObjects = renderList.transparent;
+ const lightsNode = renderList.lightsNode;
+
+ if ( opaqueObjects.length > 0 ) this._renderObjects( opaqueObjects, camera, scene, lightsNode );
+ if ( transparentObjects.length > 0 ) this._renderObjects( transparentObjects, camera, scene, lightsNode );
+
+ // finish render pass
+
+ this.backend.finishRender( renderContext );
+
+ // restore render tree
+
+ nodeFrame.renderId = previousRenderId;
+ this._currentRenderContext = previousRenderState;
+
+ this._lastRenderContext = renderContext;
+
+ }
+
+ setAnimationLoop( callback ) {
+
+ if ( this._initialized === false ) this.init();
+
+ const animation = this._animation;
+
+ animation.setAnimationLoop( callback );
+
+ ( callback === null ) ? animation.stop() : animation.start();
+
+ }
+
+ async getArrayBuffer( attribute ) {
+
+ return await this.backend.getArrayBuffer( attribute );
+
+ }
+
+ getContext() {
+
+ return this._context;
+
+ }
+
+ getPixelRatio() {
+
+ return this._pixelRatio;
+
+ }
+
+ getDrawingBufferSize( target ) {
+
+ return target.set( this._width * this._pixelRatio, this._height * this._pixelRatio ).floor();
+
+ }
+
+ getSize( target ) {
+
+ return target.set( this._width, this._height );
+
+ }
+
+ setPixelRatio( value = 1 ) {
+
+ this._pixelRatio = value;
+
+ this.setSize( this._width, this._height, false );
+
+ }
+
+ setDrawingBufferSize( width, height, pixelRatio ) {
+
+ this._width = width;
+ this._height = height;
+
+ this._pixelRatio = pixelRatio;
+
+ this.domElement.width = Math.floor( width * pixelRatio );
+ this.domElement.height = Math.floor( height * pixelRatio );
+
+ this.setViewport( 0, 0, width, height );
+
+ if ( this._initialized ) this.backend.updateSize();
+
+ }
+
+ setSize( width, height, updateStyle = true ) {
+
+ this._width = width;
+ this._height = height;
+
+ this.domElement.width = Math.floor( width * this._pixelRatio );
+ this.domElement.height = Math.floor( height * this._pixelRatio );
+
+ if ( updateStyle === true ) {
+
+ this.domElement.style.width = width + 'px';
+ this.domElement.style.height = height + 'px';
+
+ }
+
+ this.setViewport( 0, 0, width, height );
+
+ if ( this._initialized ) this.backend.updateSize();
+
+ }
+
+ setOpaqueSort( method ) {
+
+ this._opaqueSort = method;
+
+ }
+
+ setTransparentSort( method ) {
+
+ this._transparentSort = method;
+
+ }
+
+ getScissor( target ) {
+
+ const scissor = this._scissor;
+
+ target.x = scissor.x;
+ target.y = scissor.y;
+ target.width = scissor.width;
+ target.height = scissor.height;
+
+ return target;
+
+ }
+
+ setScissor( x, y, width, height ) {
+
+ const scissor = this._scissor;
+
+ if ( x.isVector4 ) {
+
+ scissor.copy( x );
+
+ } else {
+
+ scissor.set( x, y, width, height );
+
+ }
+
+ }
+
+ getScissorTest() {
+
+ return this._scissorTest;
+
+ }
+
+ setScissorTest( boolean ) {
+
+ this._scissorTest = boolean;
+
+ }
+
+ getViewport( target ) {
+
+ return target.copy( this._viewport );
+
+ }
+
+ setViewport( x, y, width, height, minDepth = 0, maxDepth = 1 ) {
+
+ const viewport = this._viewport;
+
+ if ( x.isVector4 ) {
+
+ viewport.copy( x );
+
+ } else {
+
+ viewport.set( x, y, width, height );
+
+ }
+
+ viewport.minDepth = minDepth;
+ viewport.maxDepth = maxDepth;
+
+ }
+
+ getClearColor( target ) {
+
+ return target.copy( this._clearColor );
+
+ }
+
+ setClearColor( color, alpha = 1 ) {
+
+ this._clearColor.set( color );
+ this._clearAlpha = alpha;
+
+ }
+
+ getClearAlpha() {
+
+ return this._clearAlpha;
+
+ }
+
+ setClearAlpha( alpha ) {
+
+ this._clearAlpha = alpha;
+
+ }
+
+ getClearDepth() {
+
+ return this._clearDepth;
+
+ }
+
+ setClearDepth( depth ) {
+
+ this._clearDepth = depth;
+
+ }
+
+ getClearStencil() {
+
+ return this._clearStencil;
+
+ }
+
+ setClearStencil( stencil ) {
+
+ this._clearStencil = stencil;
+
+ }
+
+ clear( color = true, depth = true, stencil = true ) {
+
+ const renderContext = this._currentRenderContext || this._lastRenderContext;
+
+ if ( renderContext ) this.backend.clear( renderContext, color, depth, stencil );
+
+ }
+
+ clearColor() {
+
+ this.clear( true, false, false );
+
+ }
+
+ clearDepth() {
+
+ this.clear( false, true, false );
+
+ }
+
+ clearStencil() {
+
+ this.clear( false, false, true );
+
+ }
+
+ dispose() {
+
+ this._objects.dispose();
+ this._properties.dispose();
+ this._pipelines.dispose();
+ this._nodes.dispose();
+ this._bindings.dispose();
+ this._info.dispose();
+ this._renderLists.dispose();
+ this._renderContexts.dispose();
+ this._textures.dispose();
+
+ this.setRenderTarget( null );
+ this.setAnimationLoop( null );
+
+ }
+
+ setRenderTarget( renderTarget, activeCubeFace = 0 ) {
+
+ this._renderTarget = renderTarget;
+ this._activeCubeFace = activeCubeFace;
+
+ }
+
+ async compute( computeNodes ) {
+
+ if ( this._initialized === false ) await this.init();
+
+ const backend = this.backend;
+ const pipelines = this._pipelines;
+ const computeGroup = Array.isArray( computeNodes ) ? computeNodes : [ computeNodes ];
+
+ backend.beginCompute( computeGroup );
+
+ for ( const computeNode of computeGroup ) {
+
+ // onInit
+
+ if ( pipelines.has( computeNode ) === false ) {
+
+ computeNode.onInit( { renderer: this } );
+
+ }
+
+ this._nodes.updateForCompute( computeNode );
+ this._bindings.updateForCompute( computeNode );
+
+ const computePipeline = pipelines.getForCompute( computeNode );
+ const computeBindings = this._bindings.getForCompute( computeNode );
+
+ backend.compute( computeGroup, computeNode, computeBindings, computePipeline );
+
+ }
+
+ backend.finishCompute( computeGroup );
+
+ }
+
+ getRenderTarget() {
+
+ return this._renderTarget;
+
+ }
+
+ hasFeature( name ) {
+
+ return this.backend.hasFeature( name );
+
+ }
+
+ copyFramebufferToTexture( framebufferTexture ) {
+
+ const renderContext = this._currentRenderContext || this._lastRenderContext;
+
+ this._textures.updateTexture( framebufferTexture );
+
+ this.backend.copyFramebufferToTexture( framebufferTexture, renderContext );
+
+ }
+
+ _projectObject( object, camera, groupOrder, renderList ) {
+
+ if ( object.visible === false ) return;
+
+ const visible = object.layers.test( camera.layers );
+
+ if ( visible ) {
+
+ if ( object.isGroup ) {
+
+ groupOrder = object.renderOrder;
+
+ } else if ( object.isLOD ) {
+
+ if ( object.autoUpdate === true ) object.update( camera );
+
+ } else if ( object.isLight ) {
+
+ renderList.pushLight( object );
+
+ } else if ( object.isSprite ) {
+
+ if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) {
+
+ if ( this.sortObjects === true ) {
+
+ _vector3.setFromMatrixPosition( object.matrixWorld ).applyMatrix4( _projScreenMatrix );
+
+ }
+
+ const geometry = object.geometry;
+ const material = object.material;
+
+ if ( material.visible ) {
+
+ renderList.push( object, geometry, material, groupOrder, _vector3.z, null );
+
+ }
+
+ }
+
+ } else if ( object.isLineLoop ) {
+
+ console.error( 'THREE.Renderer: Objects of type THREE.LineLoop are not supported. Please use THREE.Line or THREE.LineSegments.' );
+
+ } else if ( object.isMesh || object.isLine || object.isPoints ) {
+
+ if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) {
+
+ const geometry = object.geometry;
+ const material = object.material;
+
+ if ( this.sortObjects === true ) {
+
+ if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
+
+ _vector3
+ .copy( geometry.boundingSphere.center )
+ .applyMatrix4( object.matrixWorld )
+ .applyMatrix4( _projScreenMatrix );
+
+ }
+
+ if ( Array.isArray( material ) ) {
+
+ const groups = geometry.groups;
+
+ for ( let i = 0, l = groups.length; i < l; i ++ ) {
+
+ const group = groups[ i ];
+ const groupMaterial = material[ group.materialIndex ];
+
+ if ( groupMaterial && groupMaterial.visible ) {
+
+ renderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group );
+
+ }
+
+ }
+
+ } else if ( material.visible ) {
+
+ renderList.push( object, geometry, material, groupOrder, _vector3.z, null );
+
+ }
+
+ }
+
+ }
+
+ }
+
+ const children = object.children;
+
+ for ( let i = 0, l = children.length; i < l; i ++ ) {
+
+ this._projectObject( children[ i ], camera, groupOrder, renderList );
+
+ }
+
+ }
+
+ _renderObjects( renderList, camera, scene, lightsNode ) {
+
+ // process renderable objects
+
+ for ( let i = 0, il = renderList.length; i < il; i ++ ) {
+
+ const renderItem = renderList[ i ];
+
+ // @TODO: Add support for multiple materials per object. This will require to extract
+ // the material from the renderItem object and pass it with its group data to _renderObject().
+
+ const { object, geometry, material, group } = renderItem;
+
+ if ( camera.isArrayCamera ) {
+
+ const cameras = camera.cameras;
+
+ for ( let j = 0, jl = cameras.length; j < jl; j ++ ) {
+
+ const camera2 = cameras[ j ];
+
+ if ( object.layers.test( camera2.layers ) ) {
+
+ const vp = camera2.viewport;
+ const minDepth = ( vp.minDepth === undefined ) ? 0 : vp.minDepth;
+ const maxDepth = ( vp.maxDepth === undefined ) ? 1 : vp.maxDepth;
+
+ const viewportValue = this._currentRenderContext.viewportValue;
+ viewportValue.copy( vp ).multiplyScalar( this._pixelRatio ).floor();
+ viewportValue.minDepth = minDepth;
+ viewportValue.maxDepth = maxDepth;
+
+ this.backend.updateViewport( this._currentRenderContext );
+
+ this._renderObject( object, scene, camera2, geometry, material, group, lightsNode );
+
+ }
+
+ }
+
+ } else {
+
+ this._renderObject( object, scene, camera, geometry, material, group, lightsNode );
+
+ }
+
+ }
+
+ }
+
+ _renderObject( object, scene, camera, geometry, material, group, lightsNode ) {
+
+ material = scene.overrideMaterial !== null ? scene.overrideMaterial : material;
+
+ //
+
+ object.onBeforeRender( this, scene, camera, geometry, material, group );
+
+ //
+
+ const renderObject = this._objects.get( object, material, scene, camera, lightsNode );
+ renderObject.context = this._currentRenderContext;
+
+ //
+
+ this._nodes.updateBefore( renderObject );
+
+ //
+
+ object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
+ object.normalMatrix.getNormalMatrix( object.modelViewMatrix );
+
+ //
+
+ this._nodes.updateForRender( renderObject );
+ this._geometries.update( renderObject );
+ this._bindings.updateForRender( renderObject );
+
+ //
+
+ this.backend.draw( renderObject, this._info );
+
+ }
+
+}
+
+export default Renderer;
diff --git a/examples/jsm/renderers/webgpu/WebGPUBackend.js b/examples/jsm/renderers/webgpu/WebGPUBackend.js
new file mode 100644
index 00000000000000..4b41c2372297c6
--- /dev/null
+++ b/examples/jsm/renderers/webgpu/WebGPUBackend.js
@@ -0,0 +1,841 @@
+/*// debugger tools
+import 'https://greggman.github.io/webgpu-avoid-redundant-state-setting/webgpu-check-redundant-state-setting.js';
+//*/
+
+import { GPUFeatureName, GPUTextureFormat, GPULoadOp, GPUStoreOp, GPUIndexFormat, GPUTextureViewDimension } from './utils/WebGPUConstants.js';
+
+import WebGPUNodeBuilder from './nodes/WGSLNodeBuilder.js';
+import Backend from '../common/Backend.js';
+
+import { DepthTexture, DepthFormat, DepthStencilFormat, UnsignedInt248Type, UnsignedIntType, WebGPUCoordinateSystem } from 'three';
+
+import WebGPUUtils from './utils/WebGPUUtils.js';
+import WebGPUAttributeUtils from './utils/WebGPUAttributeUtils.js';
+import WebGPUBindingUtils from './utils/WebGPUBindingUtils.js';
+import WebGPUPipelineUtils from './utils/WebGPUPipelineUtils.js';
+import WebGPUTextureUtils from './utils/WebGPUTextureUtils.js';
+
+// statics
+
+let _staticAdapter = null;
+
+if ( navigator.gpu !== undefined ) {
+
+ _staticAdapter = await navigator.gpu.requestAdapter();
+
+}
+
+//
+
+class WebGPUBackend extends Backend {
+
+ constructor( parameters = {} ) {
+
+ super( parameters );
+
+ // some parameters require default values other than "undefined"
+
+ this.parameters.antialias = ( parameters.antialias === true );
+
+ if ( this.parameters.antialias === true ) {
+
+ this.parameters.sampleCount = ( parameters.sampleCount === undefined ) ? 4 : parameters.sampleCount;
+
+ } else {
+
+ this.parameters.sampleCount = 1;
+
+ }
+
+ this.parameters.requiredLimits = ( parameters.requiredLimits === undefined ) ? {} : parameters.requiredLimits;
+
+ this.adapter = null;
+ this.device = null;
+ this.context = null;
+ this.colorBuffer = null;
+
+ this.depthBuffers = new WeakMap();
+
+ this.utils = new WebGPUUtils( this );
+ this.attributeUtils = new WebGPUAttributeUtils( this );
+ this.bindingUtils = new WebGPUBindingUtils( this );
+ this.pipelineUtils = new WebGPUPipelineUtils( this );
+ this.textureUtils = new WebGPUTextureUtils( this );
+
+ }
+
+ async init( renderer ) {
+
+ await super.init( renderer );
+
+ //
+
+ const parameters = this.parameters;
+
+ const adapterOptions = {
+ powerPreference: parameters.powerPreference
+ };
+
+ const adapter = await navigator.gpu.requestAdapter( adapterOptions );
+
+ if ( adapter === null ) {
+
+ throw new Error( 'WebGPUBackend: Unable to create WebGPU adapter.' );
+
+ }
+
+ // feature support
+
+ const features = Object.values( GPUFeatureName );
+
+ const supportedFeatures = [];
+
+ for ( const name of features ) {
+
+ if ( adapter.features.has( name ) ) {
+
+ supportedFeatures.push( name );
+
+ }
+
+ }
+
+ const deviceDescriptor = {
+ requiredFeatures: supportedFeatures,
+ requiredLimits: parameters.requiredLimits
+ };
+
+ const device = await adapter.requestDevice( deviceDescriptor );
+
+ const context = ( parameters.context !== undefined ) ? parameters.context : renderer.domElement.getContext( 'webgpu' );
+
+ this.adapter = adapter;
+ this.device = device;
+ this.context = context;
+
+ this.updateSize();
+
+ }
+
+ get coordinateSystem() {
+
+ return WebGPUCoordinateSystem;
+
+ }
+
+ async getArrayBuffer( attribute ) {
+
+ return await this.attributeUtils.getArrayBuffer( attribute );
+
+ }
+
+ beginRender( renderContext ) {
+
+ const renderContextData = this.get( renderContext );
+
+ const device = this.device;
+
+ const descriptor = {
+ colorAttachments: [ {
+ view: null
+ } ],
+ depthStencilAttachment: {
+ view: null
+ }
+ };
+
+ const colorAttachment = descriptor.colorAttachments[ 0 ];
+ const depthStencilAttachment = descriptor.depthStencilAttachment;
+
+ const antialias = this.parameters.antialias;
+
+ if ( renderContext.texture !== null ) {
+
+ const textureData = this.get( renderContext.texture );
+ const depthTextureData = this.get( renderContext.depthTexture );
+
+ // @TODO: Support RenderTarget with antialiasing.
+
+ colorAttachment.view = textureData.texture.createView( {
+ baseMipLevel: 0,
+ mipLevelCount: 1,
+ baseArrayLayer: renderContext.activeCubeFace,
+ dimension: GPUTextureViewDimension.TwoD
+ } );
+
+ depthStencilAttachment.view = depthTextureData.texture.createView();
+
+ if ( renderContext.stencil && renderContext.depthTexture.format === DepthFormat ) {
+
+ renderContext.stencil = false;
+
+ }
+
+ } else {
+
+ if ( antialias === true ) {
+
+ colorAttachment.view = this.colorBuffer.createView();
+ colorAttachment.resolveTarget = this.context.getCurrentTexture().createView();
+
+ } else {
+
+ colorAttachment.view = this.context.getCurrentTexture().createView();
+ colorAttachment.resolveTarget = undefined;
+
+ }
+
+ depthStencilAttachment.view = this._getDepthBufferGPU( renderContext ).createView();
+
+ }
+
+ if ( renderContext.clearColor ) {
+
+ colorAttachment.clearValue = renderContext.clearColorValue;
+ colorAttachment.loadOp = GPULoadOp.Clear;
+ colorAttachment.storeOp = GPUStoreOp.Store;
+
+ } else {
+
+ colorAttachment.loadOp = GPULoadOp.Load;
+ colorAttachment.storeOp = GPUStoreOp.Store;
+
+ }
+
+ //
+
+ if ( renderContext.depth ) {
+
+ if ( renderContext.clearDepth ) {
+
+ depthStencilAttachment.depthClearValue = renderContext.clearDepthValue;
+ depthStencilAttachment.depthLoadOp = GPULoadOp.Clear;
+ depthStencilAttachment.depthStoreOp = GPUStoreOp.Store;
+
+ } else {
+
+ depthStencilAttachment.depthLoadOp = GPULoadOp.Load;
+ depthStencilAttachment.depthStoreOp = GPUStoreOp.Store;
+
+ }
+
+ }
+
+ if ( renderContext.stencil ) {
+
+ if ( renderContext.clearStencil ) {
+
+ depthStencilAttachment.stencilClearValue = renderContext.clearStencilValue;
+ depthStencilAttachment.stencilLoadOp = GPULoadOp.Clear;
+ depthStencilAttachment.stencilStoreOp = GPUStoreOp.Store;
+
+ } else {
+
+ depthStencilAttachment.stencilLoadOp = GPULoadOp.Load;
+ depthStencilAttachment.stencilStoreOp = GPUStoreOp.Store;
+
+ }
+
+ }
+
+ //
+
+ const encoder = device.createCommandEncoder( { label: 'renderContext_' + renderContext.id } );
+ const currentPass = encoder.beginRenderPass( descriptor );
+
+ //
+
+ renderContextData.descriptor = descriptor;
+ renderContextData.encoder = encoder;
+ renderContextData.currentPass = currentPass;
+ renderContextData.currentAttributesSet = {};
+
+ //
+
+ if ( renderContext.viewport ) {
+
+ this.updateViewport( renderContext );
+
+ }
+
+ if ( renderContext.scissor ) {
+
+ const { x, y, width, height } = renderContext.scissorValue;
+
+ currentPass.setScissorRect( x, y, width, height );
+
+ }
+
+ }
+
+ finishRender( renderContext ) {
+
+ const renderContextData = this.get( renderContext );
+
+ renderContextData.currentPass.end();
+
+ this.device.queue.submit( [ renderContextData.encoder.finish() ] );
+
+ //
+
+ if ( renderContext.texture !== null && renderContext.texture.generateMipmaps === true ) {
+
+ this.textureUtils.generateMipmaps( renderContext.texture );
+
+ }
+
+ }
+
+ updateViewport( renderContext ) {
+
+ const { currentPass } = this.get( renderContext );
+ const { x, y, width, height, minDepth, maxDepth } = renderContext.viewportValue;
+
+ currentPass.setViewport( x, y, width, height, minDepth, maxDepth );
+
+ }
+
+ clear( renderContext, color, depth, stencil ) {
+
+ const device = this.device;
+ const renderContextData = this.get( renderContext );
+
+ const { descriptor } = renderContextData;
+
+ depth = depth && renderContext.depth;
+ stencil = stencil && renderContext.stencil;
+
+ const colorAttachment = descriptor.colorAttachments[ 0 ];
+
+ const antialias = this.parameters.antialias;
+
+ // @TODO: Include render target in clear operation.
+ if ( antialias === true ) {
+
+ colorAttachment.view = this.colorBuffer.createView();
+ colorAttachment.resolveTarget = this.context.getCurrentTexture().createView();
+
+ } else {
+
+ colorAttachment.view = this.context.getCurrentTexture().createView();
+ colorAttachment.resolveTarget = undefined;
+
+ }
+
+ descriptor.depthStencilAttachment.view = this._getDepthBufferGPU( renderContext ).createView();
+
+ if ( color ) {
+
+ colorAttachment.loadOp = GPULoadOp.Clear;
+ colorAttachment.clearValue = renderContext.clearColorValue;
+
+ }
+
+ if ( depth ) {
+
+ descriptor.depthStencilAttachment.depthLoadOp = GPULoadOp.Clear;
+ descriptor.depthStencilAttachment.depthClearValue = renderContext.clearDepthValue;
+
+ }
+
+ if ( stencil ) {
+
+ descriptor.depthStencilAttachment.stencilLoadOp = GPULoadOp.Clear;
+ descriptor.depthStencilAttachment.stencilClearValue = renderContext.clearStencilValue;
+
+ }
+
+ renderContextData.encoder = device.createCommandEncoder( {} );
+ renderContextData.currentPass = renderContextData.encoder.beginRenderPass( descriptor );
+
+ renderContextData.currentPass.end();
+
+ device.queue.submit( [ renderContextData.encoder.finish() ] );
+
+ }
+
+ // compute
+
+ beginCompute( computeGroup ) {
+
+ const groupGPU = this.get( computeGroup );
+
+ groupGPU.cmdEncoderGPU = this.device.createCommandEncoder( {} );
+ groupGPU.passEncoderGPU = groupGPU.cmdEncoderGPU.beginComputePass();
+
+ }
+
+ compute( computeGroup, computeNode, bindings, pipeline ) {
+
+ const { passEncoderGPU } = this.get( computeGroup );
+
+ // pipeline
+
+ const pipelineGPU = this.get( pipeline ).pipeline;
+ passEncoderGPU.setPipeline( pipelineGPU );
+
+ // bind group
+
+ const bindGroupGPU = this.get( bindings ).group;
+ passEncoderGPU.setBindGroup( 0, bindGroupGPU );
+
+ passEncoderGPU.dispatchWorkgroups( computeNode.dispatchCount );
+
+ }
+
+ finishCompute( computeGroup ) {
+
+ const groupData = this.get( computeGroup );
+
+ groupData.passEncoderGPU.end();
+ this.device.queue.submit( [ groupData.cmdEncoderGPU.finish() ] );
+
+ }
+
+ // render object
+
+ draw( renderObject, info ) {
+
+ const { object, geometry, context, pipeline } = renderObject;
+
+ const bindingsData = this.get( renderObject.getBindings() );
+ const contextData = this.get( context );
+ const pipelineGPU = this.get( pipeline ).pipeline;
+ const attributesSet = contextData.currentAttributesSet;
+
+ // pipeline
+
+ const passEncoderGPU = contextData.currentPass;
+ passEncoderGPU.setPipeline( pipelineGPU );
+
+ // bind group
+
+ const bindGroupGPU = bindingsData.group;
+ passEncoderGPU.setBindGroup( 0, bindGroupGPU );
+
+ // attributes
+
+ const index = renderObject.getIndex();
+
+ const hasIndex = ( index !== null );
+
+ // index
+
+ if ( hasIndex === true ) {
+
+ if ( attributesSet.index !== index ) {
+
+ const buffer = this.get( index ).buffer;
+ const indexFormat = ( index.array instanceof Uint16Array ) ? GPUIndexFormat.Uint16 : GPUIndexFormat.Uint32;
+
+ passEncoderGPU.setIndexBuffer( buffer, indexFormat );
+
+ attributesSet.index = index;
+
+ }
+
+ }
+
+ // vertex buffers
+
+ const attributes = renderObject.getAttributes();
+
+ for ( let i = 0, l = attributes.length; i < l; i ++ ) {
+
+ const attribute = attributes[ i ];
+
+ if ( attributesSet[ i ] !== attribute ) {
+
+ const buffer = this.get( attribute ).buffer;
+ passEncoderGPU.setVertexBuffer( i, buffer );
+
+ attributesSet[ i ] = attribute;
+
+ }
+
+ }
+
+ // draw
+
+ const drawRange = geometry.drawRange;
+ const firstVertex = drawRange.start;
+
+ const instanceCount = this.getInstanceCount( renderObject );
+
+ if ( hasIndex === true ) {
+
+ const indexCount = ( drawRange.count !== Infinity ) ? drawRange.count : index.count;
+
+ passEncoderGPU.drawIndexed( indexCount, instanceCount, firstVertex, 0, 0 );
+
+ info.update( object, indexCount, instanceCount );
+
+ } else {
+
+ const positionAttribute = geometry.attributes.position;
+ const vertexCount = ( drawRange.count !== Infinity ) ? drawRange.count : positionAttribute.count;
+
+ passEncoderGPU.draw( vertexCount, instanceCount, firstVertex, 0 );
+
+ info.update( object, vertexCount, instanceCount );
+
+ }
+
+ }
+
+ // cache key
+
+ needsUpdate( renderObject ) {
+
+ const renderObjectGPU = this.get( renderObject );
+
+ const { object, material } = renderObject;
+
+ const utils = this.utils;
+
+ const sampleCount = utils.getSampleCount( renderObject.context );
+ const colorSpace = utils.getCurrentColorSpace( renderObject.context );
+ const colorFormat = utils.getCurrentColorFormat( renderObject.context );
+ const depthStencilFormat = utils.getCurrentDepthStencilFormat( renderObject.context );
+ const primitiveTopology = utils.getPrimitiveTopology( object, material );
+
+ let needsUpdate = false;
+
+ if ( renderObjectGPU.sampleCount !== sampleCount || renderObjectGPU.colorSpace !== colorSpace ||
+ renderObjectGPU.colorFormat !== colorFormat || renderObjectGPU.depthStencilFormat !== depthStencilFormat ||
+ renderObjectGPU.primitiveTopology !== primitiveTopology ) {
+
+ renderObjectGPU.sampleCount = sampleCount;
+ renderObjectGPU.colorSpace = colorSpace;
+ renderObjectGPU.colorFormat = colorFormat;
+ renderObjectGPU.depthStencilFormat = depthStencilFormat;
+ renderObjectGPU.primitiveTopology = primitiveTopology;
+
+ needsUpdate = true;
+
+ }
+
+ return needsUpdate;
+
+ }
+
+ getCacheKey( renderObject ) {
+
+ const { object, material } = renderObject;
+
+ const utils = this.utils;
+ const renderContext = renderObject.context;
+
+ return [
+ utils.getSampleCount( renderContext ),
+ utils.getCurrentColorSpace( renderContext ), utils.getCurrentColorFormat( renderContext ), utils.getCurrentDepthStencilFormat( renderContext ),
+ utils.getPrimitiveTopology( object, material )
+ ].join();
+
+ }
+
+ // textures
+
+ createSampler( texture ) {
+
+ this.textureUtils.createSampler( texture );
+
+ }
+
+ destroySampler( texture ) {
+
+ this.textureUtils.destroySampler( texture );
+
+ }
+
+ createDefaultTexture( texture ) {
+
+ this.textureUtils.createDefaultTexture( texture );
+
+ }
+
+ createTexture( texture ) {
+
+ this.textureUtils.createTexture( texture );
+
+ }
+
+ updateTexture( texture ) {
+
+ this.textureUtils.updateTexture( texture );
+
+ }
+
+ destroyTexture( texture ) {
+
+ this.textureUtils.destroyTexture( texture );
+
+ }
+
+ // node builder
+
+ createNodeBuilder( object, renderer ) {
+
+ return new WebGPUNodeBuilder( object, renderer );
+
+ }
+
+ // program
+
+ createProgram( program ) {
+
+ const programGPU = this.get( program );
+
+ programGPU.module = {
+ module: this.device.createShaderModule( { code: program.code, label: program.stage } ),
+ entryPoint: 'main'
+ };
+
+ }
+
+ destroyProgram( program ) {
+
+ this.delete( program );
+
+ }
+
+ // pipelines
+
+ createRenderPipeline( renderObject ) {
+
+ this.pipelineUtils.createRenderPipeline( renderObject );
+
+ }
+
+ createComputePipeline( computePipeline ) {
+
+ this.pipelineUtils.createComputePipeline( computePipeline );
+
+ }
+
+ // bindings
+
+ createBindings( bindings, pipeline ) {
+
+ this.bindingUtils.createBindings( bindings, pipeline );
+
+ }
+
+ updateBindings( bindings, pipeline ) {
+
+ this.bindingUtils.createBindings( bindings, pipeline );
+
+ }
+
+ updateBinding( binding ) {
+
+ this.bindingUtils.updateBinding( binding );
+
+ }
+
+ // attributes
+
+ createIndexAttribute( attribute ) {
+
+ this.attributeUtils.createAttribute( attribute, GPUBufferUsage.INDEX | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST );
+
+ }
+
+ createAttribute( attribute ) {
+
+ this.attributeUtils.createAttribute( attribute, GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST );
+
+ }
+
+ createStorageAttribute( attribute ) {
+
+ this.attributeUtils.createAttribute( attribute, GPUBufferUsage.STORAGE | GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST );
+
+ }
+
+ updateAttribute( attribute ) {
+
+ this.attributeUtils.updateAttribute( attribute );
+
+ }
+
+ destroyAttribute( attribute ) {
+
+ this.attributeUtils.destroyAttribute( attribute );
+
+ }
+
+ // canvas
+
+ updateSize() {
+
+ this._configureContext();
+ this._setupColorBuffer();
+
+ }
+
+ // utils public
+
+ hasFeature( name ) {
+
+ const adapter = this.adapter || _staticAdapter;
+
+ //
+
+ const features = Object.values( GPUFeatureName );
+
+ if ( features.includes( name ) === false ) {
+
+ throw new Error( 'THREE.WebGPURenderer: Unknown WebGPU GPU feature: ' + name );
+
+ }
+
+ //
+
+ return adapter.features.has( name );
+
+ }
+
+ copyFramebufferToTexture( texture, renderContext ) {
+
+ const renderContextData = this.get( renderContext );
+
+ const { encoder, descriptor } = renderContextData;
+
+ let sourceGPU = null;
+
+ if ( texture.isFramebufferTexture ) {
+
+ sourceGPU = this.context.getCurrentTexture();
+
+ } else if ( texture.isDepthTexture ) {
+
+ sourceGPU = this._getDepthBufferGPU( renderContext );
+
+ }
+
+ const destinationGPU = this.get( texture ).texture;
+
+ renderContextData.currentPass.end();
+
+ encoder.copyTextureToTexture(
+ {
+ texture: sourceGPU,
+ origin: { x: 0, y: 0, z: 0 }
+ },
+ {
+ texture: destinationGPU
+ },
+ [
+ texture.image.width,
+ texture.image.height
+ ]
+ );
+
+ if ( texture.generateMipmaps ) this.textureUtils.generateMipmaps( texture );
+
+ descriptor.colorAttachments[ 0 ].loadOp = GPULoadOp.Load;
+ if ( renderContext.depth ) descriptor.depthStencilAttachment.depthLoadOp = GPULoadOp.Load;
+ if ( renderContext.stencil ) descriptor.depthStencilAttachment.stencilLoadOp = GPULoadOp.Load;
+
+ renderContextData.currentPass = encoder.beginRenderPass( descriptor );
+ renderContextData.currentAttributesSet = {};
+
+ }
+
+ // utils
+
+ _getDepthBufferGPU( renderContext ) {
+
+ const { depthBuffers } = this;
+ const { width, height } = this.getDrawingBufferSize();
+
+ let depthTexture = depthBuffers.get( renderContext );
+
+ if ( depthTexture !== undefined && depthTexture.image.width === width && depthTexture.image.height === height ) {
+
+ return this.get( depthTexture ).texture;
+
+ }
+
+ this._destroyDepthBufferGPU( renderContext );
+
+ depthTexture = new DepthTexture();
+ depthTexture.name = 'depthBuffer';
+
+ if ( renderContext.stencil ) {
+
+ depthTexture = new DepthTexture();
+ depthTexture.format = DepthStencilFormat;
+ depthTexture.type = UnsignedInt248Type;
+
+ } else if ( renderContext.depth ) {
+
+ depthTexture = new DepthTexture();
+ depthTexture.format = DepthFormat;
+ depthTexture.type = UnsignedIntType;
+
+ }
+
+ depthTexture.image.width = width;
+ depthTexture.image.height = height;
+
+ this.textureUtils.createTexture( depthTexture, { sampleCount: this.parameters.sampleCount } );
+
+ depthBuffers.set( renderContext, depthTexture );
+
+ return this.get( depthTexture ).texture;
+
+ }
+
+ _destroyDepthBufferGPU( renderContext ) {
+
+ const { depthBuffers } = this;
+
+ const depthTexture = depthBuffers.get( renderContext );
+
+ if ( depthTexture !== undefined ) {
+
+ this.textureUtils.destroyTexture( depthTexture );
+
+ depthBuffers.delete( renderContext );
+
+ }
+
+ }
+
+ _configureContext() {
+
+ this.context.configure( {
+ device: this.device,
+ format: GPUTextureFormat.BGRA8Unorm,
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,
+ alphaMode: 'premultiplied'
+ } );
+
+ }
+
+ _setupColorBuffer() {
+
+ if ( this.colorBuffer ) this.colorBuffer.destroy();
+
+ const { width, height } = this.getDrawingBufferSize();
+ //const format = navigator.gpu.getPreferredCanvasFormat(); // @TODO: Move to WebGPUUtils
+
+ this.colorBuffer = this.device.createTexture( {
+ label: 'colorBuffer',
+ size: {
+ width: width,
+ height: height,
+ depthOrArrayLayers: 1
+ },
+ sampleCount: this.parameters.sampleCount,
+ format: GPUTextureFormat.BGRA8Unorm,
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC
+ } );
+
+ }
+
+}
+
+export default WebGPUBackend;
diff --git a/examples/jsm/renderers/webgpu/WebGPUTextures.js b/examples/jsm/renderers/webgpu/WebGPUTextures.js
index ba6e7196b1f408..b85aeffd353b16 100644
--- a/examples/jsm/renderers/webgpu/WebGPUTextures.js
+++ b/examples/jsm/renderers/webgpu/WebGPUTextures.js
@@ -1,476 +1,405 @@
-import { GPUTextureFormat, GPUAddressMode, GPUFilterMode, GPUTextureDimension, GPUFeatureName } from './constants.js';
-import { VideoTexture, CubeTexture, Texture, NearestFilter, NearestMipmapNearestFilter, NearestMipmapLinearFilter, LinearFilter, RepeatWrapping, MirroredRepeatWrapping, RGB_ETC2_Format, RGBA_ETC2_EAC_Format,
- RGBAFormat, RedFormat, RGFormat, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, UnsignedByteType, FloatType, HalfFloatType, SRGBColorSpace, DepthFormat, DepthStencilFormat, DepthTexture,
+import {
+ GPUTextureFormat, GPUAddressMode, GPUFilterMode, GPUTextureDimension, GPUFeatureName
+} from './WebGPUConstants.js';
+
+import {
+ CubeTexture, Texture,
+ NearestFilter, NearestMipmapNearestFilter, NearestMipmapLinearFilter, LinearFilter,
+ RepeatWrapping, MirroredRepeatWrapping,
+ RGB_ETC2_Format, RGBA_ETC2_EAC_Format,
+ RGBAFormat, RedFormat, RGFormat, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, UnsignedByteType, FloatType, HalfFloatType, SRGBColorSpace, DepthFormat, DepthStencilFormat,
RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_ASTC_10x5_Format,
- RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_10x10_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, UnsignedIntType, UnsignedShortType, UnsignedInt248Type
+ RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_10x10_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, UnsignedIntType, UnsignedShortType, UnsignedInt248Type,
+ NeverCompare, AlwaysCompare, LessCompare, LessEqualCompare, EqualCompare, GreaterEqualCompare, GreaterCompare, NotEqualCompare
} from 'three';
-import WebGPUTextureUtils from './WebGPUTextureUtils.js';
-class WebGPUTextures {
+import { CubeReflectionMapping, CubeRefractionMapping, EquirectangularReflectionMapping, EquirectangularRefractionMapping } from 'three';
- constructor( device, properties, info ) {
+import WebGPUTextureMipmapUtils from './WebGPUTextureMipmapUtils.js';
- this.device = device;
- this.properties = properties;
- this.info = info;
+const _compareToWebGPU = {
+ [ NeverCompare ]: 'never',
+ [ AlwaysCompare ]: 'less',
+ [ LessCompare ]: 'equal',
+ [ LessEqualCompare ]: 'less-equal',
+ [ EqualCompare ]: 'greater',
+ [ GreaterEqualCompare ]: 'not-equal',
+ [ GreaterCompare ]: 'greater-equal',
+ [ NotEqualCompare ]: 'always'
+};
- this.defaultTexture = null;
- this.depthDefaultTexture = null;
- this.defaultVideoTexture = null;
- this.defaultCubeTexture = null;
- this.defaultSampler = null;
-
- this.samplerCache = new Map();
- this.utils = null;
-
- }
-
- getDefaultSampler() {
+class WebGPUTextureUtils {
- if ( this.defaultSampler === null ) {
+ constructor( backend ) {
- this.defaultSampler = this.device.createSampler( {} );
+ this.backend = backend;
- }
+ this.mipmapUtils = null;
- return this.defaultSampler;
+ this.defaultTexture = null;
+ this.defaultCubeTexture = null;
}
- getDefaultDepthTexture() {
-
- if ( this.depthDefaultTexture === null ) {
+ createSampler( texture ) {
- const depthTexture = new DepthTexture();
- depthTexture.image.width = 1;
- depthTexture.image.height = 1;
+ const backend = this.backend;
+ const device = backend.device;
- this._uploadTexture( depthTexture );
+ const textureGPU = backend.get( texture );
- this.depthDefaultTexture = this.getTextureGPU( depthTexture );
-
- }
-
- return this.depthDefaultTexture;
-
- }
-
- getDefaultTexture() {
-
- if ( this.defaultTexture === null ) {
-
- const texture = new Texture();
- texture.minFilter = NearestFilter;
- texture.magFilter = NearestFilter;
+ const samplerDescriptorGPU = {
+ addressModeU: this._convertAddressMode( texture.wrapS ),
+ addressModeV: this._convertAddressMode( texture.wrapT ),
+ addressModeW: this._convertAddressMode( texture.wrapR ),
+ magFilter: this._convertFilterMode( texture.magFilter ),
+ minFilter: this._convertFilterMode( texture.minFilter ),
+ mipmapFilter: this._convertFilterMode( texture.minFilter ),
+ maxAnisotropy: texture.anisotropy
+ };
- this._uploadTexture( texture );
+ if ( texture.isDepthTexture && texture.compareFunction !== null ) {
- this.defaultTexture = this.getTextureGPU( texture );
+ samplerDescriptorGPU.compare = _compareToWebGPU[ texture.compareFunction ];
}
- return this.defaultTexture;
+ textureGPU.sampler = device.createSampler( samplerDescriptorGPU );
}
- getDefaultVideoTexture() {
+ createDefaultTexture( texture ) {
- if ( this.defaultVideoTexture === null ) {
+ let textureGPU;
- const video = document.getElementById( 'video' );
+ if ( texture.isCubeTexture ) {
- const texture = new VideoTexture( video );
- texture.minFilter = NearestFilter;
- texture.magFilter = NearestFilter;
+ textureGPU = this._getDefaultCubeTextureGPU();
- this._uploadVideoTexture( texture );
+ } else {
- this.defaultVideoTexture = this.getTextureGPU( texture );
+ textureGPU = this._getDefaultTextureGPU();
}
- return this.defaultVideoTexture;
+ this.backend.get( texture ).texture = textureGPU;
}
- getDefaultCubeTexture() {
+ createTexture( texture, options = {} ) {
- if ( this.defaultCubeTexture === null ) {
-
- const texture = new CubeTexture();
- texture.minFilter = NearestFilter;
- texture.magFilter = NearestFilter;
+ const backend = this.backend;
+ const textureData = backend.get( texture );
- this._uploadTexture( texture );
+ if ( textureData.initialized ) {
- this.defaultCubeTexture = this.getTextureGPU( texture );
+ throw new Error( 'WebGPUTextureUtils: Texture already initialized.' );
}
- return this.defaultCubeTexture;
+ const { width, height, depth } = this._getSize( texture );
- }
+ const needsMipmaps = this._needsMipmaps( texture );
+ const dimension = this._getDimension( texture );
+ const mipLevelCount = this._getMipLevelCount( texture, width, height, needsMipmaps );
+ const format = texture.internalFormat || this._getFormat( texture );
+ //const sampleCount = texture.isRenderTargetTexture || texture.isDepthTexture ? backend.utils.getSampleCount( renderContext ) : 1;
+ const sampleCount = options.sampleCount !== undefined ? options.sampleCount : 1;
- getTextureGPU( texture ) {
+ let usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.COPY_SRC;
- const textureProperties = this.properties.get( texture );
+ if ( texture.isCompressedTexture !== true ) {
- return textureProperties.textureGPU;
+ usage |= GPUTextureUsage.RENDER_ATTACHMENT;
- }
+ }
- getSampler( texture ) {
+ const textureDescriptorGPU = {
+ label: texture.name,
+ size: {
+ width: width,
+ height: height,
+ depthOrArrayLayers: depth,
+ },
+ mipLevelCount: mipLevelCount,
+ sampleCount: sampleCount,
+ dimension: dimension,
+ format: format,
+ usage: usage
+ };
- const textureProperties = this.properties.get( texture );
+ // texture creation
- return textureProperties.samplerGPU;
+ if ( texture.isVideoTexture ) {
- }
+ const video = texture.source.data;
+ const videoFrame = new VideoFrame( video );
- updateTexture( texture ) {
+ textureDescriptorGPU.size.width = videoFrame.displayWidth;
+ textureDescriptorGPU.size.height = videoFrame.displayHeight;
- let needsUpdate = false;
+ videoFrame.close();
- const textureProperties = this.properties.get( texture );
+ textureData.externalTexture = video;
- if ( texture.version > 0 && textureProperties.version !== texture.version ) {
+ } else {
- const image = texture.image;
+ textureData.texture = backend.device.createTexture( textureDescriptorGPU );
- if ( image === undefined ) {
+ }
- console.warn( 'THREE.WebGPURenderer: Texture marked for update but image is undefined.' );
+ textureData.initialized = true;
- } else if ( image.complete === false ) {
+ textureData.needsMipmaps = needsMipmaps;
+ textureData.textureDescriptorGPU = textureDescriptorGPU;
- console.warn( 'THREE.WebGPURenderer: Texture marked for update but image is incomplete.' );
+ }
- } else {
+ destroyTexture( texture ) {
- // texture init
+ const backend = this.backend;
+ const textureData = backend.get( texture );
- if ( textureProperties.initialized === undefined ) {
+ textureData.texture.destroy();
- textureProperties.initialized = true;
+ backend.delete( texture );
- const disposeCallback = onTextureDispose.bind( this );
- textureProperties.disposeCallback = disposeCallback;
+ }
- texture.addEventListener( 'dispose', disposeCallback );
+ destroySampler( texture ) {
- this.info.memory.textures ++;
+ const backend = this.backend;
+ const textureData = backend.get( texture );
- }
+ delete textureData.sampler;
- //
+ }
- if ( texture.isVideoTexture ) {
+ generateMipmaps( texture ) {
- needsUpdate = this._uploadVideoTexture( texture );
+ const textureData = this.backend.get( texture );
- } else {
+ if ( texture.isCubeTexture ) {
- needsUpdate = this._uploadTexture( texture );
+ for ( let i = 0; i < 6; i ++ ) {
- }
+ this._generateMipmaps( textureData.texture, textureData.textureDescriptorGPU, i );
}
- }
-
- // if the texture is used for RTT, it's necessary to init it once so the binding
- // group's resource definition points to the respective GPUTexture
-
- if ( textureProperties.initializedRTT === false ) {
+ } else {
- textureProperties.initializedRTT = true;
- needsUpdate = true;
+ this._generateMipmaps( textureData.texture, textureData.textureDescriptorGPU );
}
- return needsUpdate;
-
}
- updateSampler( texture ) {
+ updateTexture( texture ) {
- const array = [];
+ const textureData = this.backend.get( texture );
- array.push( texture.wrapS );
- array.push( texture.wrapT );
- array.push( texture.wrapR );
- array.push( texture.magFilter );
- array.push( texture.minFilter );
- array.push( texture.anisotropy );
+ const { needsMipmaps, textureDescriptorGPU } = textureData;
- const key = array.join();
- let samplerGPU = this.samplerCache.get( key );
+ // transfer texture data
- if ( samplerGPU === undefined ) {
+ if ( texture.isDataTexture || texture.isDataArrayTexture || texture.isData3DTexture ) {
- samplerGPU = this.device.createSampler( {
- addressModeU: this._convertAddressMode( texture.wrapS ),
- addressModeV: this._convertAddressMode( texture.wrapT ),
- addressModeW: this._convertAddressMode( texture.wrapR ),
- magFilter: this._convertFilterMode( texture.magFilter ),
- minFilter: this._convertFilterMode( texture.minFilter ),
- mipmapFilter: this._convertFilterMode( texture.minFilter ),
- maxAnisotropy: texture.anisotropy
- } );
+ this._copyBufferToTexture( texture.image, textureData.texture, textureDescriptorGPU, needsMipmaps );
- this.samplerCache.set( key, samplerGPU );
+ } else if ( texture.isCompressedTexture ) {
- }
+ this._copyCompressedBufferToTexture( texture.mipmaps, textureData.texture, textureDescriptorGPU );
- const textureProperties = this.properties.get( texture );
- textureProperties.samplerGPU = samplerGPU;
+ } else if ( texture.isCubeTexture ) {
- }
+ if ( texture.image.length === 6 ) {
- initRenderTarget( renderTarget ) {
+ this._copyCubeMapToTexture( texture.image, texture, textureData.texture, textureDescriptorGPU, needsMipmaps );
- const properties = this.properties;
- const renderTargetProperties = properties.get( renderTarget );
+ }
- if ( renderTargetProperties.initialized === undefined ) {
+ } else if ( texture.isRenderTargetTexture ) {
- const device = this.device;
+ if ( needsMipmaps === true ) this._generateMipmaps( textureData.texture, textureDescriptorGPU );
- const width = renderTarget.width;
- const height = renderTarget.height;
+ } else if ( texture.isVideoTexture ) {
- const texture = renderTarget.texture;
+ const video = texture.source.data;
- const colorTextureFormat = texture.internalFormat || this._getFormat( texture );
- const label = texture.name ? '_' + texture.name : '';
- const needsMipmaps = this._needsMipmaps( texture );
- const mipLevelCount = this._getMipLevelCount( texture, width, height, needsMipmaps );
+ textureData.externalTexture = video;
- const colorTextureGPU = device.createTexture( {
- label: 'renderTarget' + label,
- size: {
- width: width,
- height: height,
- depthOrArrayLayers: 1
- },
- mipLevelCount: mipLevelCount,
- format: colorTextureFormat,
- usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST
- } );
+ } else if ( texture.image !== null ) {
- this.info.memory.textures ++;
+ this._copyImageToTexture( texture.image, texture, textureData.texture, textureDescriptorGPU, needsMipmaps );
- renderTargetProperties.colorTextureGPU = colorTextureGPU;
- renderTargetProperties.colorTextureFormat = colorTextureFormat;
+ } else {
- // When the ".texture" or ".depthTexture" property of a render target is used as a map,
- // the renderer has to find the respective GPUTexture objects to setup the bind groups.
- // Since it's not possible to see just from a texture object whether it belongs to a render
- // target or not, we need the initializedRTT flag.
+ console.warn( 'WebGPUTextureUtils: Unable to update texture.' );
- const textureProperties = properties.get( texture );
- textureProperties.textureGPU = colorTextureGPU;
- textureProperties.initializedRTT = false;
+ }
- if ( renderTarget.depthBuffer === true ) {
+ //
- const depthTextureFormat = renderTarget.depthTexture !== null ? this._getFormat( renderTarget.depthTexture ) : GPUTextureFormat.Depth24PlusStencil8;
+ textureData.version = texture.version;
- const depthTextureGPU = device.createTexture( {
- label: 'renderTarget' + label + '_depthBuffer',
- size: {
- width: width,
- height: height,
- depthOrArrayLayers: 1
- },
- format: depthTextureFormat,
- usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST
- } );
+ }
- this.info.memory.textures ++;
+ _isEnvironmentTexture( texture ) {
- renderTargetProperties.depthTextureGPU = depthTextureGPU;
- renderTargetProperties.depthTextureFormat = depthTextureFormat;
+ const mapping = texture.mapping;
- if ( renderTarget.depthTexture !== null ) {
+ return ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ) || ( mapping === CubeReflectionMapping || mapping === CubeRefractionMapping );
- const depthTextureProperties = properties.get( renderTarget.depthTexture );
- depthTextureProperties.textureGPU = depthTextureGPU;
- depthTextureProperties.initializedRTT = false;
+ }
- }
+ _getDefaultTextureGPU() {
- }
+ let defaultTexture = this.defaultTexture;
- //
+ if ( defaultTexture === null ) {
- const disposeCallback = onRenderTargetDispose.bind( this );
- renderTargetProperties.disposeCallback = disposeCallback;
-
- renderTarget.addEventListener( 'dispose', disposeCallback );
+ const texture = new Texture();
+ texture.minFilter = NearestFilter;
+ texture.magFilter = NearestFilter;
- //
+ this.createTexture( texture );
- renderTargetProperties.initialized = true;
+ this.defaultTexture = defaultTexture = texture;
}
- }
-
- dispose() {
-
- this.samplerCache.clear();
+ return this.backend.get( defaultTexture ).texture;
}
- _convertAddressMode( value ) {
+ _getDefaultCubeTextureGPU() {
- let addressMode = GPUAddressMode.ClampToEdge;
+ let defaultCubeTexture = this.defaultTexture;
- if ( value === RepeatWrapping ) {
+ if ( defaultCubeTexture === null ) {
- addressMode = GPUAddressMode.Repeat;
+ const texture = new CubeTexture();
+ texture.minFilter = NearestFilter;
+ texture.magFilter = NearestFilter;
- } else if ( value === MirroredRepeatWrapping ) {
+ this.createTexture( texture );
- addressMode = GPUAddressMode.MirrorRepeat;
+ this.defaultCubeTexture = defaultCubeTexture = texture;
}
- return addressMode;
+ return this.backend.get( defaultCubeTexture ).texture;
}
- _convertFilterMode( value ) {
-
- let filterMode = GPUFilterMode.Linear;
-
- if ( value === NearestFilter || value === NearestMipmapNearestFilter || value === NearestMipmapLinearFilter ) {
-
- filterMode = GPUFilterMode.Nearest;
+ _copyImageToTexture( image, texture, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth ) {
- }
-
- return filterMode;
+ if ( this._isHTMLImage( image ) ) {
- }
+ this._getImageBitmapFromHTML( image, texture ).then( imageBitmap => {
- _uploadVideoTexture( texture ) {
+ this._copyExternalImageToTexture( imageBitmap, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth );
- const device = this.device;
+ } );
- const textureProperties = this.properties.get( texture );
+ } else {
- const textureGPU = device.importExternalTexture( {
- source: texture.source.data
- } );
+ // assume ImageBitmap
- textureProperties.textureGPU = textureGPU;
- //textureProperties.version = texture.version; // @TODO: Force update for now, study a better solution soon using native VideoTexture.update() to fix warns
+ this._copyExternalImageToTexture( image, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth );
- return true;
+ }
}
- _uploadTexture( texture ) {
-
- let needsUpdate = false;
-
- const device = this.device;
- const image = texture.image;
-
- const textureProperties = this.properties.get( texture );
-
- const { width, height, depth } = this._getSize( texture );
- const needsMipmaps = this._needsMipmaps( texture );
- const dimension = this._getDimension( texture );
- const mipLevelCount = this._getMipLevelCount( texture, width, height, needsMipmaps );
- const format = texture.internalFormat || this._getFormat( texture );
-
- let usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST;
+ _isHTMLImage( image ) {
- if ( needsMipmaps ) {
+ return ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement );
- // current mipmap generation requires RENDER_ATTACHMENT
+ }
- usage |= GPUTextureUsage.RENDER_ATTACHMENT;
+ _copyCubeMapToTexture( images, texture, textureGPU, textureDescriptorGPU, needsMipmaps ) {
- }
+ for ( let i = 0; i < 6; i ++ ) {
- const textureGPUDescriptor = {
- label: texture.name,
- size: {
- width: width,
- height: height,
- depthOrArrayLayers: depth,
- },
- mipLevelCount: mipLevelCount,
- sampleCount: 1,
- dimension: dimension,
- format: format,
- usage: usage
- };
+ const image = images[ i ];
- // texture creation
+ if ( image.isDataTexture ) {
- let textureGPU = textureProperties.textureGPU;
+ this._copyBufferToTexture( image.image, textureGPU, textureDescriptorGPU, needsMipmaps, i );
- if ( textureGPU === undefined ) {
+ } else {
- textureGPU = device.createTexture( textureGPUDescriptor );
+ this._copyImageToTexture( image, texture, textureGPU, textureDescriptorGPU, needsMipmaps, i );
- needsUpdate = true;
+ }
}
- // transfer texture data
+ }
- if ( texture.isDataTexture || texture.isDataArrayTexture || texture.isData3DTexture ) {
+ _copyExternalImageToTexture( image, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth = 0 ) {
- this._copyBufferToTexture( image, textureGPU, textureGPUDescriptor, needsMipmaps );
+ const device = this.backend.device;
- } else if ( texture.isCompressedTexture ) {
+ device.queue.copyExternalImageToTexture(
+ {
+ source: image
+ }, {
+ texture: textureGPU,
+ mipLevel: 0,
+ origin: { x: 0, y: 0, z: originDepth }
+ }, {
+ width: image.width,
+ height: image.height,
+ depthOrArrayLayers: 1
+ }
+ );
- this._copyCompressedBufferToTexture( texture.mipmaps, textureGPU, textureGPUDescriptor );
+ if ( needsMipmaps ) this._generateMipmaps( textureGPU, textureDescriptorGPU, originDepth );
- } else if ( texture.isCubeTexture ) {
+ }
- if ( image.length === 6 ) {
+ _generateMipmaps( textureGPU, textureDescriptorGPU, baseArrayLayer = 0 ) {
- this._copyCubeMapToTexture( image, texture, textureGPU, textureGPUDescriptor, needsMipmaps );
+ if ( this.mipmapUtils === null ) {
- }
+ this.mipmapUtils = new WebGPUTextureMipmapUtils( this.backend.device );
- } else if ( texture.isRenderTargetTexture ) {
+ }
- if ( needsMipmaps === true ) this._generateMipmaps( textureGPU, textureGPUDescriptor );
+ this.mipmapUtils.generateMipmaps( textureGPU, textureDescriptorGPU, baseArrayLayer );
- } else if ( texture.isDepthTexture !== true && image !== null ) {
+ }
- this._copyImageToTexture( image, texture, textureGPU, textureGPUDescriptor, needsMipmaps );
+ _getImageBitmapFromHTML( image, texture ) {
- }
+ const width = image.width;
+ const height = image.height;
- //
+ const options = {};
- textureProperties.textureGPU = textureGPU;
- textureProperties.version = texture.version;
+ options.imageOrientation = ( texture.flipY === true ) ? 'flipY' : 'none';
+ options.premultiplyAlpha = ( texture.premultiplyAlpha === true ) ? 'premultiply' : 'default';
- return needsUpdate;
+ return createImageBitmap( image, 0, 0, width, height, options );
}
- _copyBufferToTexture( image, textureGPU, textureGPUDescriptor, needsMipmaps, originDepth = 0 ) {
+ _copyBufferToTexture( image, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth = 0 ) {
// @TODO: Consider to use GPUCommandEncoder.copyBufferToTexture()
// @TODO: Consider to support valid buffer layouts with other formats like RGB
+ const device = this.backend.device;
+
const data = image.data;
- const bytesPerTexel = this._getBytesPerTexel( textureGPUDescriptor.format );
+ const bytesPerTexel = this._getBytesPerTexel( textureDescriptorGPU.format );
const bytesPerRow = image.width * bytesPerTexel;
- this.device.queue.writeTexture(
+ device.queue.writeTexture(
{
texture: textureGPU,
mipLevel: 0,
@@ -487,55 +416,17 @@ class WebGPUTextures {
depthOrArrayLayers: ( image.depth !== undefined ) ? image.depth : 1
} );
- if ( needsMipmaps === true ) this._generateMipmaps( textureGPU, textureGPUDescriptor, originDepth );
+ if ( needsMipmaps === true ) this._generateMipmaps( textureGPU, textureDescriptorGPU, originDepth );
}
- _copyCubeMapToTexture( images, texture, textureGPU, textureGPUDescriptor, needsMipmaps ) {
-
- for ( let i = 0; i < 6; i ++ ) {
-
- const image = images[ i ];
-
- if ( image.isDataTexture ) {
-
- this._copyBufferToTexture( image.image, textureGPU, textureGPUDescriptor, needsMipmaps, i );
-
- } else {
-
- this._copyImageToTexture( image, texture, textureGPU, textureGPUDescriptor, needsMipmaps, i );
-
- }
-
- }
-
- }
-
- _copyExternalImageToTexture( image, textureGPU, textureGPUDescriptor, needsMipmaps, originDepth = 0 ) {
-
- this.device.queue.copyExternalImageToTexture(
- {
- source: image
- }, {
- texture: textureGPU,
- mipLevel: 0,
- origin: { x: 0, y: 0, z: originDepth }
- }, {
- width: image.width,
- height: image.height,
- depthOrArrayLayers: 1
- }
- );
-
- if ( needsMipmaps ) this._generateMipmaps( textureGPU, textureGPUDescriptor, originDepth );
-
- }
-
- _copyCompressedBufferToTexture( mipmaps, textureGPU, textureGPUDescriptor ) {
+ _copyCompressedBufferToTexture( mipmaps, textureGPU, textureDescriptorGPU ) {
// @TODO: Consider to use GPUCommandEncoder.copyBufferToTexture()
- const blockData = this._getBlockData( textureGPUDescriptor.format );
+ const device = this.backend.device;
+
+ const blockData = this._getBlockData( textureDescriptorGPU.format );
for ( let i = 0; i < mipmaps.length; i ++ ) {
@@ -546,7 +437,7 @@ class WebGPUTextures {
const bytesPerRow = Math.ceil( width / blockData.width ) * blockData.byteLength;
- this.device.queue.writeTexture(
+ device.queue.writeTexture(
{
texture: textureGPU,
mipLevel: i
@@ -567,18 +458,6 @@ class WebGPUTextures {
}
- _generateMipmaps( textureGPU, textureGPUDescriptor, baseArrayLayer ) {
-
- if ( this.utils === null ) {
-
- this.utils = new WebGPUTextureUtils( this.device ); // only create this helper if necessary
-
- }
-
- this.utils.generateMipmaps( textureGPU, textureGPUDescriptor, baseArrayLayer );
-
- }
-
_getBlockData( format ) {
// this method is only relevant for compressed texture formats
@@ -616,6 +495,76 @@ class WebGPUTextures {
}
+ _convertAddressMode( value ) {
+
+ let addressMode = GPUAddressMode.ClampToEdge;
+
+ if ( value === RepeatWrapping ) {
+
+ addressMode = GPUAddressMode.Repeat;
+
+ } else if ( value === MirroredRepeatWrapping ) {
+
+ addressMode = GPUAddressMode.MirrorRepeat;
+
+ }
+
+ return addressMode;
+
+ }
+
+ _convertFilterMode( value ) {
+
+ let filterMode = GPUFilterMode.Linear;
+
+ if ( value === NearestFilter || value === NearestMipmapNearestFilter || value === NearestMipmapLinearFilter ) {
+
+ filterMode = GPUFilterMode.Nearest;
+
+ }
+
+ return filterMode;
+
+ }
+
+ _getSize( texture ) {
+
+ const image = texture.image;
+
+ let width, height, depth;
+
+ if ( texture.isCubeTexture ) {
+
+ const faceImage = image.length > 0 ? image[ 0 ].image || image[ 0 ] : null;
+
+ width = faceImage ? faceImage.width : 1;
+ height = faceImage ? faceImage.height : 1;
+ depth = 6; // one image for each side of the cube map
+
+ } else if ( image !== null ) {
+
+ width = image.width;
+ height = image.height;
+ depth = ( image.depth !== undefined ) ? image.depth : 1;
+
+ } else {
+
+ width = height = depth = 1;
+
+ }
+
+ return { width, height, depth };
+
+ }
+
+ _needsMipmaps( texture ) {
+
+ if ( this._isEnvironmentTexture( texture ) ) return true;
+
+ return ( texture.isCompressedTexture !== true ) /*&& ( texture.generateMipmaps === true )*/ && ( texture.minFilter !== NearestFilter ) && ( texture.minFilter !== LinearFilter );
+
+ }
+
_getBytesPerTexel( format ) {
if ( format === GPUTextureFormat.R8Unorm ) return 1;
@@ -648,6 +597,28 @@ class WebGPUTextures {
}
+ _getMipLevelCount( texture, width, height, needsMipmaps ) {
+
+ let mipLevelCount;
+
+ if ( texture.isCompressedTexture ) {
+
+ mipLevelCount = texture.mipmaps.length;
+
+ } else if ( needsMipmaps ) {
+
+ mipLevelCount = Math.floor( Math.log2( Math.max( width, height ) ) ) + 1;
+
+ } else {
+
+ mipLevelCount = 1; // a texture without mipmaps has a base mip (mipLevel 0)
+
+ }
+
+ return mipLevelCount;
+
+ }
+
_getFormat( texture ) {
const format = texture.format;
@@ -656,7 +627,11 @@ class WebGPUTextures {
let formatGPU;
- if ( texture.isCompressedTexture === true ) {
+ if ( /*texture.isRenderTargetTexture === true ||*/ texture.isFramebufferTexture === true ) {
+
+ formatGPU = GPUTextureFormat.BGRA8Unorm;
+
+ } else if ( texture.isCompressedTexture === true ) {
switch ( format ) {
@@ -875,176 +850,6 @@ class WebGPUTextures {
}
- _isHTMLImage( image ) {
-
- return ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement );
-
- }
-
- _copyImageToTexture( image, texture, textureGPU, textureGPUDescriptor, needsMipmaps, originDepth ) {
-
- if ( this._isHTMLImage( image ) ) {
-
- this._getImageBitmapFromHTML( image, texture ).then( imageBitmap => {
-
- this._copyExternalImageToTexture( imageBitmap, textureGPU, textureGPUDescriptor, needsMipmaps, originDepth );
-
- } );
-
- } else {
-
- // assume ImageBitmap
-
- this._copyExternalImageToTexture( image, textureGPU, textureGPUDescriptor, needsMipmaps, originDepth );
-
- }
-
- }
-
- _getImageBitmapFromHTML( image, texture ) {
-
- const width = image.width;
- const height = image.height;
-
- const options = {};
-
- options.imageOrientation = ( texture.flipY === true ) ? 'flipY' : 'none';
- options.premultiplyAlpha = ( texture.premultiplyAlpha === true ) ? 'premultiply' : 'default';
-
- return createImageBitmap( image, 0, 0, width, height, options );
-
- }
-
- _getImageBitmap( image, texture ) {
-
- const width = image.width;
- const height = image.height;
-
- if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
- ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ) {
-
- const options = {};
-
- options.imageOrientation = ( texture.flipY === true ) ? 'flipY' : 'none';
- options.premultiplyAlpha = ( texture.premultiplyAlpha === true ) ? 'premultiply' : 'default';
-
- return createImageBitmap( image, 0, 0, width, height, options );
-
- } else {
-
- // assume ImageBitmap
-
- return Promise.resolve( image );
-
- }
-
- }
-
- _getMipLevelCount( texture, width, height, needsMipmaps ) {
-
- let mipLevelCount;
-
- if ( texture.isCompressedTexture ) {
-
- mipLevelCount = texture.mipmaps.length;
-
- } else if ( needsMipmaps ) {
-
- mipLevelCount = Math.floor( Math.log2( Math.max( width, height ) ) ) + 1;
-
- } else {
-
- mipLevelCount = 1; // a texture without mipmaps has a base mip (mipLevel 0)
-
- }
-
- return mipLevelCount;
-
- }
-
- _getSize( texture ) {
-
- const image = texture.image;
-
- let width, height, depth;
-
- if ( texture.isCubeTexture ) {
-
- const faceImage = image.length > 0 ? image[ 0 ].image || image[ 0 ] : null;
-
- width = faceImage ? faceImage.width : 1;
- height = faceImage ? faceImage.height : 1;
- depth = 6; // one image for each side of the cube map
-
- } else if ( image !== null ) {
-
- width = image.width;
- height = image.height;
- depth = ( image.depth !== undefined ) ? image.depth : 1;
-
- } else {
-
- width = height = depth = 1;
-
- }
-
- return { width, height, depth };
-
- }
-
- _needsMipmaps( texture ) {
-
- return ( texture.isCompressedTexture !== true ) && ( texture.generateMipmaps === true ) && ( texture.minFilter !== NearestFilter ) && ( texture.minFilter !== LinearFilter );
-
- }
-
-}
-
-function onRenderTargetDispose( event ) {
-
- const renderTarget = event.target;
- const properties = this.properties;
-
- const renderTargetProperties = properties.get( renderTarget );
-
- renderTarget.removeEventListener( 'dispose', renderTargetProperties.disposeCallback );
-
- renderTargetProperties.colorTextureGPU.destroy();
- properties.remove( renderTarget.texture );
-
- this.info.memory.textures --;
-
- if ( renderTarget.depthBuffer === true ) {
-
- renderTargetProperties.depthTextureGPU.destroy();
-
- this.info.memory.textures --;
-
- if ( renderTarget.depthTexture !== null ) {
-
- properties.remove( renderTarget.depthTexture );
-
- }
-
- }
-
- properties.remove( renderTarget );
-
-}
-
-function onTextureDispose( event ) {
-
- const texture = event.target;
-
- const textureProperties = this.properties.get( texture );
- textureProperties.textureGPU.destroy();
-
- texture.removeEventListener( 'dispose', textureProperties.disposeCallback );
-
- this.properties.remove( texture );
-
- this.info.memory.textures --;
-
}
-export default WebGPUTextures;
+export default WebGPUTextureUtils;
diff --git a/examples/jsm/renderers/webgpu/utils/WebGPUUtils.js b/examples/jsm/renderers/webgpu/utils/WebGPUUtils.js
new file mode 100644
index 00000000000000..39d8d85d5b2990
--- /dev/null
+++ b/examples/jsm/renderers/webgpu/utils/WebGPUUtils.js
@@ -0,0 +1,92 @@
+import { GPUPrimitiveTopology, GPUTextureFormat } from './WebGPUConstants.js';
+
+class WebGPUUtils {
+
+ constructor( backend ) {
+
+ this.backend = backend;
+
+ }
+
+ getCurrentDepthStencilFormat( renderContext ) {
+
+ let format;
+
+ if ( renderContext.depthTexture !== null ) {
+
+ format = this.getTextureFormatGPU( renderContext.depthTexture );
+
+ } else if ( renderContext.depth && renderContext.stencil ) {
+
+ format = GPUTextureFormat.Depth24PlusStencil8;
+
+ } else if ( renderContext.depth ) {
+
+ format = GPUTextureFormat.Depth24Plus;
+
+ }
+
+ return format;
+
+ }
+
+ getTextureFormatGPU( texture ) {
+
+ return this.backend.get( texture ).texture.format;
+
+ }
+
+ getCurrentColorFormat( renderContext ) {
+
+ let format;
+
+ if ( renderContext.texture !== null ) {
+
+ format = this.getTextureFormatGPU( renderContext.texture );
+
+ } else {
+
+ format = GPUTextureFormat.BGRA8Unorm; // default context format
+
+ }
+
+ return format;
+
+ }
+
+ getCurrentColorSpace( renderContext ) {
+
+ if ( renderContext.texture !== null ) {
+
+ return renderContext.texture.colorSpace;
+
+ }
+
+ return this.backend.renderer.outputColorSpace;
+
+ }
+
+ getPrimitiveTopology( object, material ) {
+
+ if ( object.isPoints ) return GPUPrimitiveTopology.PointList;
+ else if ( object.isLineSegments || ( object.isMesh && material.wireframe === true ) ) return GPUPrimitiveTopology.LineList;
+ else if ( object.isLine ) return GPUPrimitiveTopology.LineStrip;
+ else if ( object.isMesh ) return GPUPrimitiveTopology.TriangleList;
+
+ }
+
+ getSampleCount( renderContext ) {
+
+ if ( renderContext.texture !== null ) {
+
+ return 1;
+
+ }
+
+ return this.backend.parameters.sampleCount;
+
+ }
+
+}
+
+export default WebGPUUtils;
diff --git a/examples/screenshots/webgpu_backdrop_area.jpg b/examples/screenshots/webgpu_backdrop_area.jpg
new file mode 100644
index 00000000000000..001fe203e0b94e
Binary files /dev/null and b/examples/screenshots/webgpu_backdrop_area.jpg differ
diff --git a/examples/webgl_geometry_csg.html b/examples/webgl_geometry_csg.html
index c863661a705ef9..fee4003c3b6d01 100644
--- a/examples/webgl_geometry_csg.html
+++ b/examples/webgl_geometry_csg.html
@@ -33,8 +33,8 @@
"imports": {
"three": "../build/three.module.js",
"three/addons/": "./jsm/",
- "three-mesh-bvh": "https://unpkg.com/three-mesh-bvh@^0.5.22/build/index.module.js",
- "three-bvh-csg": "https://unpkg.com/three-bvh-csg@^0.0.4/build/index.module.js"
+ "three-mesh-bvh": "https://unpkg.com/three-mesh-bvh@0.6.0/build/index.module.js",
+ "three-bvh-csg": "https://unpkg.com/three-bvh-csg@0.0.7/build/index.module.js"
}
}
diff --git a/examples/webgpu_backdrop_area.html b/examples/webgpu_backdrop_area.html
new file mode 100644
index 00000000000000..5d5cd1a7f00d8a
--- /dev/null
+++ b/examples/webgpu_backdrop_area.html
@@ -0,0 +1,181 @@
+
+
+